<pectus.ai/>
./pectus.ai — builder framework · self-hosted · MIT

For end users

gsc

Pull organic search performance from Google Search Console into the keywords table and the gsc_daily time-series.

Type: inbound. The runner calls fetch.ts directly; the model is not invoked at fetch time. Per the inbound/outbound split, Search Console’s only user-facing surface is its Settings page. Data lands in shared tables and consumer apps (today: Content Insights’s Insights surface) read it.

What it fetches

Two grains per refresh:

  • Aggregated last 28 days per query — impressions, clicks, average position, CTR. Upserts into the keywords table under metadata.gsc_* so Pectus’s existing keyword rows pick up real-traffic numbers alongside their search_volume / difficulty / intent.
  • Daily per (query, page), last 7 days — written to gsc_daily for trend analysis. Lets the analyzer compute impression-weighted week-over-week query deltas.

What you need

  • A Search Console property (URL-prefix or Domain) the project tracks.
  • A Google service account with read access to that property. Domain properties (DNS-verified) bypass the per-property user-add and are the recommended setup. URL-prefix properties require manual user-add in the Search Console UI; see the propagation FAQ if Search Console refuses the service account email.
  • The Search Console API enabled in your Google Cloud project.

The Google service account section of Set up your accounts covers all three.

Configuration

The CMS surface is one Settings page at /brands/<slug>/projects/<code>/apps/gsc/settings. It embeds the service account JSON paste, the site URL field (https://example.com/ or sc-domain:example.com), a Test connection button, and a Refresh now button that runs the fetch pipeline. The shared /brands/<slug>/settings/integrations/google page is the canonical credential store; the GSC Settings page reads from it and offers a write-through form when missing.

project_data_freshness.surface = 'gsc' records the last-refreshed timestamp. Clicking Analyze now in Insights auto-triggers a refresh if that timestamp is older than 24 hours.

Import from CSV (fallback when the API path is blocked)

A known Google bug occasionally rejects new service-account emails when you try to add them to Search Console properties — sometimes for hours, sometimes for days. The CMS GSC settings page has an Import from CSV section with two upload slots that unblock the brand without waiting for Google to heal.

  • Queries CSV. Export from Search Console: Performance → Queries tab → set your 28-day window → Export → CSV. Columns expected: Top queries, Clicks, Impressions, CTR, Position. Upserts into the keywords table under metadata.gsc_* — same shape fetchGsc writes, so Stage 1 reads the imported data identically to fetched data.
  • Pages CSV. Performance → Pages tab → Export → CSV. Columns expected: Top pages, Clicks, Impressions, CTR, Position. Upserts into the gsc_pages table (added in migration 0007_gsc_pages.sql) for future page-level analysis.

Both stamp project_data_freshness.gsc so the Insights flow treats the data as fresh and doesn’t try to auto-fetch on the next Analyze. Header matching is fuzzy across common GSC export variants; numbers tolerate locale formatting (commas, percent signs). Re-imports are idempotent — duplicates are skipped.

The Queries CSV path is enough to populate the Stage 1 keyword analysis. The Pages CSV is stored for when Stage 1’s page analysis extends to combine GA4 + GSC page signals. Day-grain data isn’t covered by the CSV path (Search Console’s Dates export doesn’t include per-query rows) so the Weekly Query Movers section stays empty until either the service-account path heals or the OAuth path lands.

What reads the data

cms/src/lib/insights/gather-gsc-daily.ts reads gsc_daily for the last 14 days and computes impression-weighted week-over-week deltas per query (this 7 days vs prior 7 days). Returns the top 8 risers and top 8 decliners with a 5-impression noise floor; both impressions and average position deltas are reported.

Stage 1 of the Insights analysis turns those into the Weekly query movers section of the interpretation panel — Rising and Declining tables with a one-sentence interpretation per query. The keyword-level GSC aggregates (in keywords.metadata) feed Stage 1’s keyword analysis directly.

Where it lives

pectusai/apps/gsc/APP.md