Skip to main content

Web Health & Growth Report — 2026-05-07

Although filed under web-seo-reports, this report covers five lenses, not just SEO: search visibility, marketing landing-page effectiveness, user funnel, engineering signals, and product/growth insights. SEO is one of the five.

TL;DR

  • Sessions are down 21% (15,408 vs 19,598) and users down 25% (11,788 vs 15,778) versus the previous 28 days — but the prior period included a March 20-26 spike that looks campaign-driven; the underlying baseline is recovering through late April.
  • Paid Search is the largest channel (41% of sessions) but Organic Search produces job applications at ~4× the rate of Paid per session (16.3 vs 3.8 apply users per 1,000 sessions, measured against the actual generate_lead event on both hostnames). Organic and Paid produce roughly equal absolute apply volume (22 vs 24 users in 28 days), even though Paid has 5× more sessions. We are under-investing in organic infrastructure.
  • Brand search is the dominant SEO query: "jod" alone is 179 clicks / 7,657 impressions, but at position 7 with 2.3% CTR — we are losing our own branded SERP. The non-brand long-tail (e.g. "dishwasher jobs singapore", "ntuc job vacancy") converts well but volumes are tiny.
  • One product surface dwarfs the rest: /jobs gets 26,596 page views from 3,845 users — 39% of all page views. The job-list page is the website. Everything else (home, FAQ, employer page) is supporting infrastructure.
  • The employer-side SEO funnel is dead: /employers has 1,273 GSC impressions but 0.7% CTR at position 13.5, and only 26 GA sessions land there. Self-serve employer acquisition through organic search is not happening today.
  • Engineering signals are not instrumented: GA4 has no Core Web Vitals custom events. We are flying blind on LCP/INP/CLS. The React audit identified concrete LCP/CLS risks (hero <img> without dimensions, render-blocking Google Fonts, AdSense without reserved height) — see Section 5.
  • We are paying Google to optimise for the wrong event. Google Ads is currently bidding against view_item_jodapp (job-detail views) — but generate_lead (actual job applies) is already firing across both hostnames (193 events / 62 users last month, split 74% Gig + 26% Career). Switching the Google Ads optimisation target to generate_lead is a single-checkbox change. See Section 4.5.

Actionable items by department

A single sortable table mapping every recommendation in this report to who owns it, how urgent it is, and the data that justifies it. Full detail and implementation notes are in Section 8.

Priority key:

  • P0 — start this sprint; blocks measurement or unlocks compounding gains
  • P1 — within 30 days; high-leverage but not blocking
  • P2 — within the quarter; hygiene + incremental optimisation
#ActionOwnerPriorityWhy (data point) & expected impact
1Fix branded SERP CTR for "jod": rewrite homepage title/meta + add Organization/WebSite JSON-LDMarketing (copy) + Eng-Web (JSON-LD)P0"jod" gets 7,657 impressions/mo at 2.3% CTR (expected 5%+ at position 7); unlocks Sitelinks; +200-300 brand clicks/mo
2Wire web-vitals package → GA4 custom events (LCP, INP, CLS, FCP, TTFB)Eng-WebP0GA4 has zero Core Web Vitals data today; Section 5 of this report had to be inferred from code review. Unblocks engineering perf decisions
3Switch Google Ads' optimisation target from view_item_jodapp (job-detail view) to generate_lead (actual apply) — single-checkbox changeMarketing (Google Ads admin)P0We are currently paying Google to drive job-detail page views, not applications. generate_lead already fires correctly on apply across both hostnames — 193 events / 62 users last month — but it is not imported as a Google Ads conversion. Switching unlocks Smart Bidding to optimise toward applies. Expected: Paid Search per-session apply rate rises from current 3.8 / 1,000 over 30-90 days as the model retrains. See §4.5
4Build programmatic SEO templates: /jobs/{role}-jobs-singapore with ItemList JSON-LDEng-Web (route + template) + Marketing (slug list) + Content (copy)P1We already rank top-5 for "dishwasher jobs singapore", "supermarket jobs in singapore", etc. with no dedicated page; +500-1,500 organic clicks/mo within 90 days
5Rescue /employers and /how-it-works: rewrite titles/meta, add internal links, audit ranking queriesMarketing (copy) + Eng-Web (route changes)P13,875 combined GSC impressions earning only 20 clicks. Self-serve employer acquisition (SGD 3K cap per LegalEntity) is gated by these pages
6Fix LCP/CLS on / and /jobs: hero <img> dimensions, fetchpriority="high", font preload, AdSense min-heightEng-WebP1These two pages = 39% of all page views (12,704 + 26,596 / 68,364). Perf gains here compound across the site
7Investigate the (not set) landing page: 328 sessions, 97% bounce, 11s durationEng-WebP1Either broken instrumentation or a real broken route — both bad. Likely a route missing <title> or losing referrer data
8Build out FAQ-style content with FAQPage JSON-LDMarketing (content) + Eng-Web (schema)P2/help/faqs already gets 656 sessions / 100% engagement organically — informational SEO is winnable. Capture "how to apply", "is jod legit", etc.
9Add Sitemap: directive to robots.txt; deprecate URL-prefix GSC propertyEng-Web (robots.txt) + Marketing (GSC admin)P2Crawler discovery (non-Googlebot) + reporting hygiene. URL-prefix property returns zero rows — confusing for analysts
10Filter Vietnam/US/HK/Germany desktop bot traffic from GA reports + Google Ads targetingMarketing/BIP2~2,100 sessions/mo (~14% of GA total) with sub-11s avg duration are bot-like; cleaner KPIs and tighter ad targeting
11Audit titles/meta for the top 15 worst-CTR pages (high-impressions, low-clicks)MarketingP2E.g. "cleaner job in singapore" 192 imp / 1 click at position 1.6; "jobs at woodlands civic centre" 261 imp / 1 click. Pure CTR optimisation
12Build cross-channel attribution: organic vs paid sessions → revenue (Listings::Job, Ads::Placement, Gig credits)Marketing/BI + Eng-API (data join)P2Today we can't tell which channel pays back. Tying GA sessions to billing data unlocks honest CAC and channel-mix decisions
13Plug /employers form-submits into the Sales pipeline (CRM hand-off + SLA)Sales + Marketing (form wiring)P1The self-serve cap is SGD 3K per LegalEntity (per project memory). Every employer that hits the cap is a Sales lead — but today we have 0 visibility on inbound employer organic + paid leads in this report
14Build a "warm-lead prospect list" from GSC employer-brand queriesSales + Marketing (data pull)P2We rank for "ntuc fairprice job vacancy", "mcdonald's job vacancy", "big sailing international pte ltd", etc. — each branded employer query is a signal that the employer's workers find us. Sales can use this list to call enterprise outbound
15Capture 3-5 employer case studies / testimonials and publish as /employers/case-studies/{slug}Sales (relationships) + Marketing (content) + Eng-Web (route)P2B2B SEO needs proof. Today /employers has only the home pitch. Case studies double as SEO long-tail (rank for {employer} hiring, {employer} reviews) and Sales decks
16Build a Sales-feedback loop: 1-question survey at end of paid employer onboarding ("what did you Google to find us?")Sales + MarketingP2Closes the loop between organic search demand and revenue. Names the keywords we should double down on

Department-level summary

  • Engineering-Web carries the most foundational weight — 5 sole-owner items + co-ownership on 7 more. Most are small (< 1 day each); the heaviest is the programmatic SEO template work (item #4).
  • Marketing-only items (1, 5, 11) are mostly copy + GSC-admin work. Doable without engineering once item #2's instrumentation lands.
  • Joint Marketing × Engineering items (2, 3, 4, 5, 6, 7, 8, 9) require coordination — schedule kickoffs together rather than passing tickets back and forth.
  • Sales has 4 items (13-16) — all about closing the loop between web demand signals and the revenue pipeline. Item #13 is P1 because Sales is currently invisible to this report: there's no funnel data for inbound employer leads, which means we cannot calculate B2B CAC or channel-quality for the side of the business that produces the largest invoices.
  • Backend / Eng-API has minimal direct work — items #4 (sitemap data) and #12 (billing data join) are the only API-side asks, and both are read-only data exposure.

Methodology

Period (current)2026-04-09 → 2026-05-04 (28 days, ending 3 days before report date due to GSC's data-finalization lag)
Period (comparison)2026-03-12 → 2026-04-08 (previous 28 days)
GA4 propertyproperties/515821279jodapp.prod.web (Singapore, SGD, Jobs & Education vertical)
GSC propertysc-domain:jodapp.com (domain property; the URL-prefix variant https://jodapp.com/ returned no rows — likely wrong canonical)
Toolsgsccli MCP (GSC Search Analytics API), mcp__analytics__* (GA4 Data API), source code review of jodapp-web routes

Limitations of this report

  • No Core Web Vitals data: GA4 property has zero custom dimensions/metrics; the web-vitals package is not wired to GA. Performance findings in Section 5 come from code review, not field data.
  • Mixed-form attribution: form_submit is a generic event covering search, login, sign-up, and apply forms. We cannot isolate "applications submitted" from it. Recommend renaming/segmenting form events in GTM before next report.
  • Bot-like sessions inflate totals: ~2,100 GA sessions (Vietnam/US/HK/Germany desktop, all with avg session duration under 11s) look like crawler traffic — see Section 1.3.
  • GSC URL-prefix property is empty but the domain property has data — recommend deprecating the URL-prefix variant to reduce confusion.
  • The GA Analytics MCP version could not be auto-verified (sandbox blocked the config-file read). The MCP is responding correctly, so this is a hygiene check, not a blocker. Run claude mcp list manually if version pinning matters.

1. Top of funnel — traffic health

1.1 Daily session trend (current vs previous 28 days)

What this shows: the previous period had two campaign-shaped spikes (March 20-21 ≈ 3,400 combined sessions, March 25-26 ≈ 2,800). The current period has no spikes but is steadily accelerating from 366 → 890 sessions/day through late April. The headline -21% is a comparison artifact; the day-7 moving average is actually trending up.

1.2 Channel mix (current period)

ChannelSessionsNew usersEngagement rateview_item_jodapp eventsForm submits
Paid Search (Google CPC)6,3165,36498.9%7,3881,078
Direct6,1374,56899.2%1,172179
Organic Search (Google)1,34849897.1%1,968245
Email25512897.3%1829
Referral (eber.co etc.)248309523
Organic Social (Telegram/WhatsApp/FB)1233216015
Organic Search (Bing)664190.9%
Cross-network5353100%9010

The story per channel:

  • Paid is the volume engine but expensive: 41% of sessions, ~$? of paid spend driving 1,078 form submits (no cost data in this report — see follow-up below).
  • Direct is suspicious: 6,137 sessions but only 1,172 view_item_jodapp events from 586 sessions. Engagement rate is 99% (high) but only 9.5% of Direct sessions reach a job detail page vs 46% for Paid. Direct likely contains app-link clicks (/app), bookmarked logins, and untagged campaigns (any jodapp.com URL shared without UTMs falls into Direct).
  • Organic Search is the highest-quality channel: smallest volume (8.7%) but 39% of sessions reach a job detail and 18.2% of organic sessions submit a form. Per dollar of marketing investment, organic is winning.
  • Email is under-utilised: 255 sessions but only 9 form submits — ratio is poor. Worth checking what's being sent (transactional vs. campaign).

1.3 Bot-like traffic

CountryDeviceSessionsAvg session durationVerdict
Vietnamdesktop8685.4sBot-like
United Statesdesktop5056.2sBot-like
Hong Kongdesktop4953.5sBot-like
Germanydesktop22210.7sBot-like
Total bot-like~2,090~13.6% of total sessions

By contrast, real engaged traffic looks like:

  • Singapore mobile: 4,925 sessions, 199.8s avg duration
  • Singapore desktop: 1,720 sessions, 153s
  • Malaysia mobile: 1,072 sessions, 238.6s

Action: filter these geos from the standard GA report and from any Google Ads Display targeting. Add a GA4 internal filter for sessions with duration < 30s from non-target geos.


2. Search visibility (SEO)

2.1 Headline GSC numbers (sc-domain:jodapp.com)

MetricCurrent 28dPrevious 28dΔ
Clicks818~700 (top 100 only)+17% (estimated)
Top query "jod" clicks179143+25%
Top query "jod" position7.16.4-0.7 (worse)
Top query "jod" CTR2.3%2.8%-0.5pp

Key dynamic: branded search demand is growing (good — marketing spend or word of mouth), but we're losing position on it (bad — competitor pages or SERP feature changes). Brand-protection SEO needs attention.

2.2 Top non-brand queries (real organic acquisition)

QueryClicksImpressionsAvg positionCTRNote
jobs on demand singapore10941.010.6%Position 1, low volume — brand-aligned
job on demand91113.98.1%Stretchable to "jobs on demand" pages
dishwasher jobs singapore7552.712.7%High intent, scalable to other roles
flexible part time jobs6381.015.8%Position 1 — under-served keyword set
gig jobs singapore51466.63.4%Page 1 but bottom — improvable
mcdonald's singapore job vacancy5172.229.4%Long-tail brand+role wins
ntuc job vacancy5435.211.6%NTUC is a crowded SERP — surprising win
supermarket jobs in singapore51002.35.0%Should be 10%+ at pos 2
dishwashing job4136.530.8%High-intent, scarce competition
flexible part time jobs (variants)4 combined101.040%Tiny volume, perfect rank

The pattern: we already rank in the top 5 for [role] jobs singapore long-tails like dishwasher / supermarket / pantry / cashiering — but we don't have dedicated landing pages for these. Today the click lands on /jobs/{company}/{location}/{role} for a specific job, which is correct for Google but we're missing the role-aggregator page that would consolidate ranking authority.

2.3 Top pages by GSC clicks

PageClicksImpressionsAvg positionCTR
/ (home)42617,1329.02.5%
/help/faqs641,7934.03.6%
gig.jodapp.com/ (subdomain)611,0898.15.6%
/contact541,5235.03.5%
/jobs/mcdonalds/.../mcdonald-s-casual-crew371,0563.23.5%
/jobs/ming-hwee-agency/hq/part-time-data-entry-...224453.24.9%
/jobs/nikuya-tanaka/.../dishwasher-pantry-support225413.94.1%
/jobs/ntuc-fairprice/.../ra-cashiering221,0444.62.1%
/rewards194673.14.1%
/bonuses162915.75.5%
/how-it-works112,6026.50.4%
/employers91,27313.50.7%

Three things to call out:

  1. Homepage CTR is low for its volume: 17,132 impressions at avg position 9 → 2.5% CTR. A site with 7+ position should be near 5-7% CTR. Hypothesis: title/meta unclear, no Sitelinks, branded search shows competitors. Action: rewrite home <title> to lead with the value prop ("Daily-pay gig & part-time jobs in Singapore" or similar) and add WebSite + SearchAction JSON-LD to enable Sitelinks search box.
  2. /how-it-works is haemorrhaging impressions — 2,602 GSC impressions, 11 clicks (0.4% CTR) at position 6.5. Either we're appearing for the wrong queries, or the snippet is unclickable. Action: pull the queries that page appears for in GSC, audit the meta title/description.
  3. The job-detail URL pattern is winning: nine of the top fifteen pages by clicks are individual /jobs/{company}/{location}/{role} pages — confirming the structured-URL strategy in docs/50-59-frontend/41-board-web/url-strategy/index.md is working.

2.4 Indexing status

  • Sitemap submitted: https://jodapp.com/sitemaps/sitemap-index.xml.gz (2025-12-25)
  • Sitemap last downloaded by Google: 2026-05-06 (yesterday — fresh)
  • URLs in sitemap: 383 submitted, 0 reported indexed in GSC

The "0 indexed" is misleading. We can see from the search-performance data that pages are being indexed (/jobs/... URLs receive impressions). The 0 is a known GSC reporting quirk for compressed sitemap indexes — it requires a full reindex cycle to populate. Verify by spot-checking with the URL Inspection tool on 5-10 sample URLs (gsccli inspect-url).

Action: also add a Sitemap: directive to public/robots.txt (currently absent per the React audit) so non-Google crawlers can discover it.


3. Marketing landing-page effectiveness

3.1 Landing pages by sessions (current period, all channels)

Landing pageSessionsAvg durationEngagement rateBounce rate
/ (home)7,511221s99.96%0.04%
/jobs1,567232s99.7%0.3%
/login1,27048s99.8%0.16%
/jobs/sg/jod-rewards711207s99.6%0.4%
/help/faqs656100s100%0%
/companies/seonggong-prestige-pte-ltd397107s100%0%
(not set)32811s2.7%97%
/contact92184s100%0%
/privacy8020s100%0%

3.2 What's working & what isn't

/jobs/sg/jod-rewards (Google Ads landing page): 711 sessions with 207s avg duration is excellent — this page is doing its job. Worth looking at as a template for other paid landings.

/companies/seonggong-prestige-pte-ltd: 397 sessions, 100% engagement, 107s duration — this single company page is over-performing. Likely a paid placement or a referral. Investigate why, because the same template (/companies/{slug}) gets a fraction of that for most other companies. If it's a backlink or a Google Ads campaign, replicate.

(not set) landing page: 328 sessions, 97% bounce, 11s duration — this is broken referrer data, likely from web views, in-app browsers, or missing <title> on a route. Bug — needs engineering investigation to figure out which route is reporting (not set).

/login 48s avg duration: short but high engagement rate — likely correct (people log in and leave to the app). Not a problem.

/employers only 26 sessions as a landing page (despite 1,273 GSC impressions): the page is not pulling its weight as an SEO door for the B2B side. SEO and product are leaving employer self-serve revenue on the table.

3.3 Organic-search-specific landing pages

GSC sends users to a different mix than the all-channel breakdown:

Landing page (Organic Search only)Sessions
/554
/jobs282
/help/faqs60
/contact55
/jobs/sg/jod-rewards32
/login24
/bonuses22
/sign-up21
/rewards17
/jobs/hrnet-venture/.../warehouse-coordinators14
/employers11

Observation: /help/faqs and /contact are accidentally being used as SEO landing pages (60 + 55 = 115 organic sessions). That's a sign people are searching things like "how do I apply" or "how to contact jod" — and we're answering those queries by accident. Worth doubling down on FAQ-style content; the FAQ page format could also rank for question-shaped queries (e.g. "how to find part-time job in Singapore"). Use FAQPage JSON-LD schema.


4. User behavior & funnel

4.1 The site has two hostnames — and the apply event lives across both

This GA4 property collects events from two hostnames. Earlier sections of this report blended them; once we split, the funnel becomes much clearer.

HostnameSessionsUsersPage viewsWhat lives here
jodapp.com12,0219,11358,213Public listing pages, marketing, careers apply (in-domain), sign-up
gig-partners.jodapp.com4,5763,60210,151Gig worker app — where Gig "Apply" clicks redirect to and complete
Cross-domain (deduped)15,40811,78868,364Totals reported in earlier sections (GA stitches sessions across hosts)

Apply flow differs by job type:

  • Gig job apply: user clicks "Apply" on jodapp.com/jobs/{slug} → redirects to gig-partners.jodapp.com → completes apply on that subdomain.
  • Career job apply (full-time/part-time/contract): stays on jodapp.com → in-domain redirects through /login and /talent/setup if needed → submits.

4.2 What generate_lead actually represents (corrected)

The previous draft of this section made two mistakes about generate_lead — first calling it a sign-up event, then claiming applications were not tracked at all. Neither is correct. Querying GA4 by hostname + pagePath shows that every single generate_lead event fires on a /jobs/... URL — meaning generate_lead is the apply event.

Hostnamegenerate_lead eventsUnique usersJob type
jodapp.com3116Career applies (in-domain)
gig-partners.jodapp.com16246Gig applies (cross-domain)
Total193~62All job applications

So applications are measured today — they were just sitting in generate_lead, split across two hostnames. Treat this event as the canonical conversion for revenue-side analysis.

form_submit event split (1,607 events total): mostly noise, useful for activity (not conversions).

BucketEventsShareWhat it is
Search~83752%Search bar on /jobs (615) + home / (222), all on jodapp.com
Auth~68342%Sign-up + login + password reset across both hostnames
Profile / misc~855%Profile completion, employer flows, post-application feedback

form_submit does not fire on the apply itself — generate_lead does. Treat form_submit as a navigation/activity signal, not a conversion.

4.3 The real conversion funnel

Real conversion rates (first time we can compute these honestly):

StageUsersRate
Sessions11,788
→ Viewed a job detail3,58730.4% of users
→ Applied to a job (generate_lead)621.7% of job-detail viewers / 0.53% overall

Apply split by job type:

  • Gig: 46 of 62 unique applicants = 74%
  • Career: 16 of 62 = 26%

Gig is 3× the apply volume of Careers. That matches the product reality — Gig is high-frequency daily-pay; Careers is lower-frequency, longer-commitment.

4.4 Channels — re-scored against actual applies, not search activity

The earlier draft of this section used form_submit/session as a conversion proxy. After the bucket breakdown above, that was mostly measuring search activity. Replacing with generate_lead (real applies) sharpens the channel story significantly:

ChannelSessionsview_item_jodapp / sessionApply users (generate_lead)Apply users / 1,000 sessions
Organic Search1,3481.462216.3
Paid Search6,3161.17243.8
Direct6,1370.19172.8
Email2550.7113.9
Unassigned/Other~1,360~34~25

Headline: Organic search produces applies at ~4× the rate of Paid Search per session (16.3 vs 3.8 per 1,000 sessions). The earlier "Organic punches above its weight" finding was true — and stronger than first reported.

The Unassigned/Other row's high rate is mostly returning users who applied via direct link without UTMs — useful to know but not actionable for acquisition.

Implication: any budget shifted from Paid Search → Organic infrastructure (programmatic SEO templates, content, page speed) should compound for at least 4× per session. This is the strongest channel-mix signal in the report.

4.5 The likely P0 hiding here — Google Ads is optimising for the wrong event

Per docs/70-79-business/74-marketing/google-ads/jodapp-conversion-tracking.md, Google Ads is currently optimising paid campaigns against view_item_jodapp — i.e. "viewed a job detail page on jodapp.com". The architect's note in the same doc says applications-submission conversions are not yet imported into Google Ads.

This means we are paying Google to drive job-detail views, not applications. A user who lands, looks, and leaves is rewarded by the algorithm; a user who lands and applies is not differentiated.

The fix is a single-checkbox change in Google Ads admin: import generate_lead from GA4 as a conversion, mark it as the primary conversion goal (or the only one), and let Google Ads' automated bidding optimise toward it instead.

Expected effect: Paid Search per-session apply rate (currently 3.8 per 1,000) should rise. The bigger compounding effect is over 30-90 days as Google Ads' Smart Bidding model retrains on apply data instead of view-page data.

This is now action item #3 in the prioritized backlog (below). It replaces the previous "instrument apply events" recommendation, which was based on the incorrect assumption that applies were not tracked.


5. Engineering signals

We have no Core Web Vitals data in GA4 — the web-vitals npm package is not wired in (verified via the GA4 custom-dimensions check, which returned an empty list). All performance findings below are from the React-engineer audit of jodapp-web.

5.1 Identified LCP / CLS risks

IssueWhereRiskFix
Hero <img> lacks width/height/fetchpriorityapp/routes/home.jsx:238 (/images/home/hero.png)High CLS + delayed LCP on home (7,511 sessions/period)Add explicit dimensions, fetchpriority="high", loading="eager"
Same on About page hero imagesapp/routes/about-page.jsx:35,65,94CLSSame fix, smaller blast radius
Google Fonts loaded as render-blocking stylesheetapp/root.jsx:28 (Inter + Plus Jakarta Sans, full weight ranges)Render blocking before LCPSelf-host critical weights, add <link rel="preload">, subset fonts
GTM injected synchronously in <head>app/root.jsx:91Adds ~50-150ms to TTFBMove to deferred load or use dangerouslySetInnerHTML after first paint
AdSense slots without reserved min-heighthome.jsx:211, 252, 388, 417CLS from ad-fill jumpsAdd min-height to ad slot containers
Heavy client-only filter UI on /jobsapp/routes/jobs/job-list-page.jsx:303Hydration cost on the most-visited page (26,596 page views)Profile + lazy-load non-critical filter components
No <link rel="preload"> for hero or fontsglobalSlows LCPAdd preloads to <head>

5.2 What to instrument before the next report

Add to app/root.jsx:

import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';

// In a useEffect after mount:
onLCP(({ name, value, id }) => gtag('event', name, { value: Math.round(value), metric_id: id }));
onINP(({ name, value, id }) => gtag('event', name, { value: Math.round(value), metric_id: id }));
onCLS(({ name, value, id }) => gtag('event', name, { value: Math.round(value * 1000), metric_id: id }));

Then create custom metrics in GA4 Admin so we can plot "75th-percentile LCP by landing page" — that is the metric Google Search uses for Page Experience ranking.

5.3 Indexing & crawl health

  • Sitemap fresh (downloaded by Google 2026-05-06).
  • robots.txt is missing the Sitemap: directive — easy fix.
  • /team/* correctly disallowed.
  • No Organization, WebSite, or BreadcrumbList JSON-LD globally — only JobPosting on detail pages. Adding Organization + WebSite SearchAction would unlock Sitelinks for the brand SERP (directly addresses the "jod" CTR problem in §2.1).

6. Pattern extraction — what works

When we line up the top 10 pages by GSC clicks against the GA landing-page leaderboard, three patterns explain success:

Pattern A: Branded company × specific role × location

Examples: /jobs/mcdonalds/.../mcdonald-s-casual-crew (37 clicks), /jobs/nikuya-tanaka/.../dishwasher-pantry-support (22 clicks), /jobs/ntuc-fairprice/.../ra-cashiering (22 clicks).

Why it works: the URL itself is keyword-rich and the JSON-LD JobPosting schema (built in app/domains/listings/job_schema_builder.rb:23) qualifies these pages for Google's Job Search experience. They rank for [role] and [company][role] long-tails simultaneously.

Replicate by: making sure every company × role × location combination has a stable URL with at least one indexable job posting. When jobs expire, redirect to the company landing rather than 404.

Pattern B: Company landing pages with relevant search intent

Example: /companies/seonggong-prestige-pte-ltd (397 sessions, 100% engagement). The architect noted these pages have search-aware metadata (title varies by ?search=<job_title>, per app/routes/companies/company-landing-page.jsx:18).

Why it works: the page captures the long tail of {company} jobs and {company} careers queries — and Singapore has a very long tail of Pte Ltd companies.

Replicate by: making sure every company in our database has a /companies/{slug} page generated, even if it has no active jobs (link to "Get notified" or to similar companies).

Pattern C: Help / informational content

Example: /help/faqs (656 sessions, 100% engagement) catches "how to use jod" and "is jod legit" queries. This is informational SEO, and we're winning it accidentally.

Replicate by: building out an editorial/help section. Specific gaps to fill:

  • "How does daily pay work in Singapore?" (we rank #1 for "daily pay job" with only 20 clicks — there's headroom)
  • "How to find a dishwasher job in Singapore"
  • Each McDonald's outlet has its own search demand — landing pages for outlet × role would aggregate authority

7. Startup-mindset insights & opportunities

7.1 The flywheel is one-sided

Per docs/30-49-domains/40-billing/overview/index.md, revenue comes from Listings::Job postings + Ads::Placement impressions + Gig credits. Both ad placements and listing visibility require organic seeker traffic to monetize.

Today, paid acquisition (Google Ads) drives 41% of seeker sessions. Every paid session that reaches a job detail page is showing ads we already paid for — paying twice for the same eyeball. Shifting even 20% of that volume to organic compounds: lower CAC + ad inventory becomes higher-margin.

7.2 The employer side is invisible

/employers is the single most important page for B2B revenue (it gates the self-serve checkout flow described in your project memory: package → T&C → auto-issued Billing::Agreement → invoice, capped at SGD 3,000). Yet:

  • 9 GSC clicks / 1,273 impressions / position 13.5 — bottom of page 1 for whatever queries it ranks for
  • Only 11 organic landing sessions in 28 days

The self-serve checkout cap (SGD 3,000 per LegalEntity) means every employer organic acquisition is potentially a SGD 3K invoice. At current ratios, organic-search to SGD 3K conversion is roughly 0%.

Action: dedicate a separate report cycle (or section in the next report) to employer-side SEO + acquisition. Different keywords ("post job free Singapore", "hire part-time staff Singapore"), different content patterns (case studies, pricing), different conversion event (generate_lead from /employers/sign-up).

7.3 Programmatic SEO is sitting on the table

We rank for dishwasher jobs singapore, flexible part time jobs, supermarket jobs in singapore, ntuc job vacancy — all without dedicated landing pages. The URL strategy doc (docs/50-59-frontend/41-board-web/url-strategy/index.md) anticipates this.

Concrete proposal: build a /jobs/{role-slug}-jobs-singapore template with:

  • An H1 of the shape [Role] jobs in Singapore (e.g. "Dishwasher jobs in Singapore")
  • Aggregated count of active listings
  • Top employers (linked to /companies/{slug})
  • Top locations (with anchor links into /jobs?location=...)
  • ItemList JSON-LD pointing to the top 10 active listings
  • Internal links from /jobs index footer

Start with the 10 roles where we already rank top-5: dishwasher, cashier, pantry, supermarket, food packer, kitchen helper, warehouse, picker, service crew, security.

Expected impact: +50-150 organic clicks/month per page once indexed, based on current long-tail SERP positions.

7.4 We're getting scraped

13.6% of GA sessions (Vietnam/US/HK/Germany desktop, sub-30s sessions) look like crawler traffic. Some of this is legit (Googlebot is in this group on some setups) but much is scraping. Two consequences:

  1. GA dashboards over-state our user base by ~14% — calibrate when reporting to leadership
  2. Job listings are likely being republished elsewhere — worth Googling exact-match snippets from a few job descriptions to see if duplicate content is showing up on competitor aggregators

Action: add a Disallow: for /jobs/*? query strings in robots.txt to discourage scraping of filtered URLs, and consider adding a noindex meta tag for paginated/filtered listing URLs (/jobs?page=2 etc.).

7.5 Search demand we don't yet serve

From the GSC query stream, queries with >100 impressions and 0-1 clicks (i.e. we appear but don't get clicked):

QueryImpressionsClicksLikely problem
job app2211Generic; we appear at pos 7.3
job on demand app831Position 3 but bad snippet
jobs on demand app971Same
daily job app521Position 2.2 — should win this
job app1951 (prev period also)Returning issue
jobs at woodlands civic centre2611Hyper-local, fixable
cleaner job in singapore1921High-intent, position 1.6 — fixable
birch forest trading pte ltd1291Brand-of-employer query

These are CTR optimisation problems, not ranking problems. Often a better <title> and <meta description> doubles CTR. Audit titles for the top 15 worst-CTR pages.

7.6 Creative growth experiments — channels we're under-amplifying

This section is the Senior Marketing Director hat — looking at the data for unconventional opportunities, not just optimisations.

A. The quiet community on chat is real — feed it. GA shows 58 organic sessions from Telegram and 51 from WhatsApp social-shares (with social_share medium) in the last 28 days. Nobody is producing this content centrally; these are workers sharing job links peer-to-peer. Two experiments worth running:

  • A WhatsApp broadcast list for "today's daily-pay gigs in Singapore" — the audience already opted in by sharing each other.
  • A Telegram channel that mirrors the top 10 daily Gig listings with a one-click "apply via Jod" deep link. Singapore blue-collar networks are heavy Telegram users.

B. Own the "daily pay" category in Singapore. We rank #1 for "daily pay job" with 20 clicks (high-intent, low volume), and #1 for "flexible part time jobs" with 6 clicks. Nobody owns this lexicon in Singapore yet. Marketing experiments:

  • Make "Daily Pay Jobs" a top-level navigation category on the home page.
  • Buy domains / 301-redirect: dailypayjobs.sg, daily-pay-jobs.sg/jobs?filter=daily_pay.
  • Run a campaign "Get paid the same day you work" on TikTok / IG Reels (audience is Gen-Z and migrant-worker mobile-first).

C. Hyperlocal landing pages — Singapore neighbourhood × role. GSC shows queries like "jobs at woodlands civic centre" (261 imp), "bukit batok jobs" (18 imp), "jobs near bukit batok" (5 imp), "jobs in supermarket" (2 imp). Singapore is small but neighbourhood-search is real.

  • Auto-generate /jobs/{neighbourhood}-jobs pages for the 30 SG neighbourhoods most represented in our listings (Yishun, Tampines, Jurong, Bukit Batok, Woodlands, etc.).
  • Each page lists active gigs within 2km, with an embedded MRT-station map.
  • ItemList JSON-LD again so they show up in Google Job Search.

D. Cluster co-marketing with top employers. Eight of our top 30 ranking pages are NTUC FairPrice outlets. McDonald's-branded queries (29 in the last 28 days, totalling 35+ clicks) show seekers want McD's-specific information. Pitch to the employer marketing team of FairPrice / NTUC / McDonald's:

  • We co-brand a /companies/ntuc-fairprice hub page with their logo and a "Hire daily-pay" partnership badge.
  • They link from their corporate "careers" page → us. We become the de facto job-marketplace for their casual hiring.
  • This converts SEO authority into a defensive moat for both sides.

E. Worker-generated content (short-form video). The Singapore blue-collar audience consumes short-form video on TikTok, IG Reels, and Xiaohongshu (Singapore Chinese-speaking workers). Cost-cheap experiment:

  • Pay 5-10 existing Gig workers SGD 50 each to record a 30-second "day-in-the-life" video at a sample shift (with employer permission).
  • Cross-post on Jod's TikTok / IG / YouTube Shorts.
  • Add the videos as embedded VideoObject JSON-LD on the matching /jobs/{role} landing page.
  • This compounds with C (hyperlocal) and B (daily-pay branding).

F. Email is leaking — re-engage or stop sending. 255 email sessions/month → 9 form submits (3.5% session-to-submit). For comparison, organic search converts at 18% session-to-submit. Either:

  • Re-segment the list (lapsed users vs. active applicants) and retarget with personalised job recommendations, or
  • Cut the spend / volume on email until we've justified it. Today it looks like noise.

G. Partnerships with community-side institutions. The blue-collar audience is reachable through community channels Marketing typically ignores:

  • People's Association (PA) community centers (RC distributing flyers).
  • Migrant-worker dormitory associations / NGOs (TWC2, HOME).
  • Polytechnic / ITE student unions (looking for daily-pay part-time jobs).
  • Mosque / temple networks (especially for Malay-speaking F&B workers).

Each is a low-cost, high-trust distribution channel for a brand that ranks #1 for "daily pay job" and is currently growing brand search +25% QoQ unprompted — there's something in the water; amplify it.


8. Recommendations — prioritized backlog

P0 — do this sprint (highest leverage)

  1. Fix branded SERP CTR for "jod"

    • Why: 7,657 impressions/month, 2.3% CTR vs expected 5%+
    • How: rewrite homepage <title> and meta-description to lead with brand + value prop; add WebSite + Organization JSON-LD in app/root.jsx; submit homepage for re-indexing in GSC
    • Owner: marketing (copy) + engineering (JSON-LD)
    • Expected impact: +200-300 clicks/month from brand alone
  2. Instrument Core Web Vitals → GA4

    • Why: we cannot answer "are pages hitting high LCP" without this; Google Search uses Page Experience as a ranking signal
    • How: install web-vitals package; wire to gtag in app/root.jsx; create GA4 custom metrics
    • Owner: engineering
    • Expected impact: enables measurement; first real data in next report
  3. Switch Google Ads' optimisation target from view_item_jodapp to generate_lead

    • Why: per docs/70-79-business/74-marketing/google-ads/jodapp-conversion-tracking.md, Google Ads currently optimises against view_item_jodapp (job-detail page view). But generate_lead (the actual apply event) is already firing correctly across both hostnames: 162 events on gig-partners.jodapp.com for Gig applies + 31 on jodapp.com for Career applies = 193 apply events from ~62 users last month. We are paying Google to drive job-page views, not applications, even though we have the application signal available
    • How: a. In GA4 Admin → Conversions → mark generate_lead as a conversion (if not already) b. In Google Ads → Tools → Conversions → Import from GA4 → select generate_lead c. Set generate_lead as the primary conversion goal for the campaigns currently running d. Optionally keep view_item_jodapp as a secondary signal for awareness campaigns
    • Owner: Marketing (Google Ads admin only — no code change)
    • Expected impact: Paid Search per-session apply rate (currently 3.8 per 1,000 sessions) should rise as Smart Bidding retrains on the new target. The compounding effect lands over 30-90 days as the model accumulates apply data
    • Caveat: Google Ads needs ~30 conversions/month minimum for Smart Bidding to optimise reliably. We have ~62 apply users/month in total, of which ~24 are from Paid. That is on the edge — if the Smart Bidding signal is too noisy, fall back to Maximise Conversions without target CPA until volume grows

P1 — within 30 days

  1. Launch programmatic role + location SEO templates

    • Why: free organic clicks already ranking top-5 with no dedicated page
    • How: new React Router route /jobs/:slug-jobs-singapore (e.g. /jobs/dishwasher-jobs-singapore); SSR'd with ItemList JSON-LD
    • Owner: engineering (templates) + content (copy) + marketing (slug list)
    • Expected impact: +500-1500 organic clicks/month within 90 days
  2. Rescue /employers and /how-it-works

    • Why: 2,602 + 1,273 = 3,875 impressions/month earning 20 clicks
    • How: rewrite titles and meta to match search intent; add internal links from homepage and footer; review what queries each page ranks for
    • Owner: marketing (copy) + engineering (route changes)
    • Expected impact: +50-150 employer-side clicks/month, supports B2B self-serve
  3. Fix LCP/CLS on the homepage and /jobs

    • Why: these two pages account for 39% of all page views; any perf gain compounds
    • How: hero-image dimensions + fetchpriority="high"; preload critical fonts; reserve min-height on AdSense slots
    • Owner: engineering
    • Expected impact: better LCP → higher Search Experience score → ranking signal lift; better CLS → fewer accidental ad clicks → cleaner UX
  4. Investigate the (not set) landing page

    • Why: 328 sessions with 97% bounce — broken instrumentation or a real broken route
    • How: filter GA debug view; identify which route emits (not set) for landingPage
    • Owner: engineering
  5. Plug /employers form-submits into the Sales pipeline (CRM hand-off + SLA)

    • Why: today this report has zero data on inbound employer leads. The self-serve cap is SGD 3K per LegalEntity (per project_self_serve_checkout memory); above the cap is Sales territory and we cannot measure it
    • How: route signup_submit from /employers/sign-up into a CRM (HubSpot / Pipedrive / whatever Sales already uses); set a 24-hour first-touch SLA; export weekly to a shared dashboard
    • Owner: Sales (process) + Marketing (form wiring)

P2 — within the quarter

  1. Build out FAQ-style content (FAQPage JSON-LD)
    • Capitalise on /help/faqs accidental success
  2. Add Sitemap: to robots.txt; deprecate the URL-prefix GSC property
  3. Geo + bot filtering in GA reports
    • Add a "real users" segment to standard report views
  4. Audit titles for the top 15 worst-CTR pages
    • Apply the brand-search lessons to long-tail
  5. Set up a proper marketing channel attribution model
    • Map sources to revenue (Listings::Job, Ads::Placement, Gig credits) — requires Billing-side instrumentation
  6. Build a "warm-lead prospect list" from GSC employer-brand queries
    • Why: every branded employer query we rank for ("ntuc fairprice job vacancy", "mcdonald's job vacancy", "big sailing international pte ltd") signals the employer's workers find us — the employer probably should know
    • How: weekly export from GSC: filter query=~/.*(pte ltd|fairprice|mcdonald|ntuc).*/i; dedupe by employer name; hand list to Sales for outbound
    • Owner: Sales (outbound) + Marketing (data pull)
  7. Capture 3-5 employer case studies and publish at /employers/case-studies/{slug}
    • Why: B2B SEO needs proof. The current /employers page is unproven copy; case studies double as content marketing and Sales collateral
    • How: Sales identifies 3-5 willing customers; Marketing ghostwrites; Eng-Web ships a route + template
    • Owner: Sales (relationships) + Marketing (content) + Eng-Web (route)
  8. Add a "what did you Google to find us?" survey at end of paid employer onboarding
    • Why: closes the loop between organic search demand and revenue. Names the keywords we should double down on
    • How: 1-question survey embedded in onboarding email or last step of self-serve checkout; results to a shared sheet
    • Owner: Sales + Marketing

9. Open questions for next report

To fill gaps that this first report cannot answer:

  1. What does CAC look like by channel? — needs Google Ads spend data joined with GA4 sessions
  2. What is the application completion rate (not just form_submit)? — needs the segmented form events from P0 #3
  3. What is the LCP/INP/CLS distribution by page? — needs the web-vitals instrumentation from P0 #2
  4. How much revenue does each top SEO page generate? — needs view_item_jodapp + Ads::Placement impression data joined; talk to BI/Metabase
  5. Is the gig.jodapp.com subdomain being indexed independently? — separate GSC property check needed
  6. Are job listings being scraped to competitor sites? — manual exact-match Google search of 10 sample listings

Appendix A: Data tables

A.1 GA4 totals — current vs previous

MetricCurrent (Apr 9 - May 4)Previous (Mar 12 - Apr 8)Δ
Sessions15,40819,598-21.4%
Total users11,78815,778-25.3%
New users10,82114,658-26.2%
Engaged sessions15,02918,867-20.3%
Engagement rate97.5%96.3%+1.2pp
Avg session duration172.1s135.1s+27.4%
Page views68,36468,994-0.9%
Bounce rate2.5%3.7%-1.3pp

Reading the deltas: fewer sessions but each session is longer and consumes more pages — quality is up while volume is down. That's a healthier-than-it-looks pattern for a 5-month-old site coming off a campaign-driven prior period.

A.2 GA4 events (current period)

EventCountUsers
page_view68,36411,644
scroll_depth57,8598,926
view_item_list (job listings shown)23,0056,106
session_start15,14711,633
ad_impression (on-page Jod ads)13,3152,549
view_item / view_item_jodapp (job detail)11,768 / 11,7663,587 / 3,586
first_visit10,82110,816
select_item9,4363,068
user_engagement7,2223,597
form_start3,6642,456
form_submit1,607423
view_search_results334198
click240192
generate_lead19362
ad_click118211

A.3 Geo + device breakdown

See Section 1.3 for bot-flagged segments. Real-traffic top:

CountryDeviceSessionsAvg duration
Singaporemobile4,925199.8s
Singaporedesktop1,720153.2s
Indiamobile2,051169.6s (out-of-market — see hreflang gap)
Malaysiamobile1,072238.6s
Philippinesmobile929187.9s

Appendix B: Sources & methodology notes

  • GSC data: pulled via gsccli_query MCP against sc-domain:jodapp.com; date range 2026-04-09 → 2026-05-04 (final data state) and 2026-03-12 → 2026-04-08 for comparison
  • GA4 data: pulled via mcp__analytics__run_report against properties/515821279; same date ranges
  • Code references: jodapp-web routes audited at /Users/alaay/jod/repo/jodapp-web/app/; jodapp-api JSON-LD builder at /Users/alaay/jod/repo/jodapp-api/app/domains/listings/job_schema_builder.rb
  • Domain context: pulled from docs/30-49-domains/, docs/50-59-frontend/41-board-web/, docs/70-79-business/74-marketing/google-tag/

Files referenced in recommendations

  • docs/50-59-frontend/41-board-web/url-strategy/index.md — URL grouping rationale
  • docs/30-49-domains/38-ads/overview/index.md — Ads marketplace contexts
  • docs/30-49-domains/40-billing/overview/index.md — Revenue mechanics
  • docs/70-79-business/74-marketing/google-ads/jodapp-conversion-tracking.mdview_item_jodapp definition
  • docs/70-79-business/74-marketing/google-tag/google-tag-overview.md — GA4 setup status

Reproducibility

To regenerate this report, the LLM session needs: GSC sc-domain:jodapp.com access, GA4 property 515821279 access, and read access to jodapp-web and jodapp-api. All MCP servers used (gsccli, mcp__analytics__*) were responding correctly.


Report prepared 2026-05-07 by Senior PM (LLM agent) using GSC + GA4 data, codebase audit, and architecture-doc review. First in this folder — sets template for monthly cadence.