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.
In the Google Cloud console, create a project or open an existing one.
Enable the Google Analytics Data API, the Google Analytics Admin API, and the Google Search Console API for the project.
Configure the OAuth consent screen for the project.
Create an OAuth client ID with the application type Web application.
Under Authorized redirect URIs, add your misomoon address followed by
/api/v1/google/callback, for examplehttps://analytics.example.com/api/v1/google/callback.Copy the client ID and the client secret.
In
apps/api, set the two secrets: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.
- Open Settings > Integrations.
- In the Import history card, click Connect Google.
- Sign in with a Google account that can read the property, and allow access. Finish within an hour of the click.
- Back in Settings, pick the property in Google property. The list shows every
property that the Google account can read, as
Account: Property. - Set the last day to import in To. Use the day before misomoon started collecting.
- To start later than the earliest day Google has, set the first day in From.
- 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.
- Open Settings > Integrations.
- In the Import history card, click Upload CSV or zip.
- 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:
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.
- Open Settings > Integrations.
- 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.
- Open Settings > Integrations.
- In the Google Search Console card, click Connect Search Console.
- Sign in with a Google account that has the site in Search Console, and allow access.
- Back in Settings, pick the property in Search Console property.
- 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
- Open Overview.
- In Where they come from, click the Organic Search channel.
- 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
- Open Settings > Integrations.
- 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
| Task | Who can do it |
|---|---|
Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET | Whoever deploys the Worker |
| See the import list, import from Google, upload CSV files, delete imports | Owner, admin, editor, and guest editor |
| Connect, change, and disconnect Search Console | Owner, admin, editor, and guest editor |
| See imported numbers and the Imported button | Everyone who can see the site's reports |
| See search terms | Everyone 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, orimported_<report>_<YYYYMMDD>_<YYYYMMDD>.csvwith 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.
dateisYYYY-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:
| Column | What it holds |
|---|---|
visitors | Unique visitors that day |
visits | Visits |
pageviews | Pageviews |
bounces | Visits that bounced |
visit_duration | The total length of all the visits, in seconds |
misomoon reads these reports. It skips files for other reports.
| Report | Columns besides date | What it fills |
|---|---|---|
visitors | The visit columns | The site totals and the chart |
sources | source, referrer, utm_source, utm_medium, utm_campaign, utm_content, utm_term, and the visit columns | source, 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.. |
pages | page, visitors, visits, pageviews, total_time_on_page, total_time_on_page_visits, total_scroll_depth, total_scroll_depth_visits | page. 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_pages | entry_page, entrances, visitors, pageviews, bounces, visit_duration | entry_page. entrances is the visit count. |
exit_pages | exit_page, exits, visitors, pageviews, bounces, visit_duration | exit_page. exits is the visit count. |
locations | country, region, and the visit columns | country as an uppercase code, and region |
browsers | browser, browser_version, and the visit columns | browser, and browser_version as the browser name followed by the version |
operating_systems | operating_system, operating_system_version, and the visit columns | os, and os_version as the system name followed by the version |
devices | device and the visit columns | device |
custom_events | name, visitors, events | event. 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.
| Dimension | From a Google property | From CSV files |
|---|---|---|
| Site totals and the chart | Yes | imported_visitors.csv |
| Custom event counts in the totals | Yes | imported_custom_events.csv |
channel | Yes | Yes |
source | Yes | Yes |
referrer | Yes | Yes |
utm_source, utm_medium, utm_campaign, utm_content, utm_term | Yes | Yes |
page | Yes | Yes |
entry_page | Yes | Yes |
exit_page | No | Yes |
country, region | Yes | Yes |
city | Yes | No |
browser | Yes | Yes |
browser_version | No | Yes |
os, os_version | Yes | Yes |
device | Yes | Yes |
event | Yes | Yes |
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:
| misomoon | |
|---|---|
| Visitors | totalUsers |
| Visits | sessions |
| Pageviews | screenPageViews |
| Bounces | sessions minus engagedSessions |
| Visit duration | userEngagementDuration |
| Time on page | userEngagementDuration divided by sessions, per pagePath |
| Events | eventCount per eventName |
source | sessionSource without www., or Direct when Google has none |
channel | sessionDefaultChannelGroup |
referrer | sessionSource when it contains a dot |
utm_source, utm_medium | sessionSource, sessionMedium |
utm_campaign, utm_content, utm_term | sessionCampaignName, sessionManualAdContent, sessionGoogleAdsKeyword |
page | pagePath, without the query string or a trailing slash |
entry_page | landingPage, without the query string or a trailing slash |
country, region, city | countryId, region, city |
device | deviceCategory as Desktop, Mobile, or Tablet |
browser | browser |
os, os_version | operatingSystem, 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:
| Field | What it holds |
|---|---|
key | The search term |
clicks | Clicks from Google search results to the site |
impressions | How often the site appeared in the results |
ctr | Clicks divided by impressions, in percent, with one decimal |
position | The average position in the results, with one decimal. 1 is the top. |
visitors | The 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:
pageandentry_pagewith==or!=, where*matches any characters, or with~or!~and one value.devicewith==or!=and one ofdesktop,mobile, ortablet.source==googleandchannel==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 path | Role | What it does |
|---|---|---|
GET imports | Editor | Lists 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 imports | Editor | Uploads a CSV export as multipart file fields. Returns 201 and the import. |
DELETE imports/:import | Editor | Deletes the import and its rows. Returns 204. |
POST imports/google/connect | Editor | Starts a Google sign-in for the import. Returns {"url": "..."} to open in the browser. |
GET imports/google/properties?grant= | Editor | Lists the Google Analytics properties that the grant can read: id, name, and account. |
POST imports/google | Editor | Starts an import from {"grant", "property", "from", "to", "label"}. property looks like properties/123. Returns 202 and {"id", "status": "importing"}. |
GET search-console | Editor | Returns available, which is true when the instance has the Google secrets, and connected, the connected property or null. |
POST search-console/connect | Editor | Starts a Google sign-in for Search Console. Returns {"url": "..."}. |
GET search-console/properties?grant= | Editor | Lists the verified Search Console properties: property, and matches, which is true for the site's domain. |
PUT search-console | Editor | Connects {"grant", "property"}. Returns the same object as GET search-console. |
DELETE search-console | Editor | Disconnects Search Console. Returns 204. |
GET /api/v1/google/callback | None | Google 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.