Skip to main content
POST
https://searchcompany-main.up.railway.app
/
api
/
cron
/
store-visibility-score
curl -X POST https://searchcompany-main.up.railway.app/api/cron/store-visibility-score \
  -H "Content-Type: application/json" \
  -H "X-API-Key: search-company" \
  -d '{
    "business_id": "nike",
    "results": [
      {
        "prompt": "What are the best running shoes?",
        "chatgpt": true,
        "claude": true,
        "gemini": false,
        "perplexity": true,
        "copilot": false,
        "deepseek": true,
        "grok": false,
        "google_ai": true
      }
    ]
  }'
{
  "status": "success",
  "message": "Visibility score 48 stored for entity abc-123",
  "overall_score": 48,
  "prompts_analyzed": 10,
  "total_true": 38,
  "platform_scores": {
    "chatgpt_score": 52,
    "claude_score": 48,
    "gemini_score": 51,
    "perplexity_score": 45,
    "copilot_score": 49,
    "deepseek_score": 47,
    "grok_score": 46,
    "google_ai_score": 50
  }
}
Internal endpoint for the Cron service to store visibility results and calculate the overall visibility score. Called ONCE per entity after all sampled prompts have been checked.

What It Does

  1. Receives prompt results from Cron (10 sampled prompts per day)
  2. Calculates overall score: total_true / (num_prompts * 8) * 100
  3. Updates TWO database tables:
    • entity_prompts_tracker - Per-prompt visibility results
    • visibility_score_history - Single row per entity with recent_score and history_scores JSONB

Score Algorithm

raw_score = (total_true_values / (num_prompts * 8)) * 100
Where total_true_values is the count of True across all checks (e.g., 10 prompts Γ— 8 platforms = 80 checks). Example: If 40 out of 80 are True β†’ raw_score = 50

Floor Protection

The score cannot regress below the historical minimum (ignoring 0s):
non_zero_scores = [s for s in historical_scores if s > 0]
min_historical = min(non_zero_scores)

if raw_score <= min_historical:
    final_score = min_historical + 1
else:
    final_score = raw_score
This ensures the visibility score trends upward over time and doesn’t regress due to temporary API failures or platform changes.

Request Body

business_id
string
required
Business identifier (org_slug)
product_id
string
Optional product/item ID
results
array
required
Array of prompt results. Each item contains:
  • prompt (string): The prompt text
  • chatgpt (boolean): Visibility on ChatGPT
  • claude (boolean): Visibility on Claude
  • gemini (boolean): Visibility on Gemini
  • perplexity (boolean): Visibility on Perplexity
  • copilot (boolean): Visibility on Copilot
  • deepseek (boolean): Visibility on DeepSeek
  • grok (boolean): Visibility on Grok
  • google_ai (boolean): Visibility on Google AI
backdate_to
string
Optional ISO date string (e.g., β€œ2025-12-13”) to backdate the score entry

Response

status
string
"success" or "error"
overall_score
integer
Calculated visibility score (0-100)
prompts_analyzed
integer
Number of prompts processed
total_true
integer
Total number of True values across all platforms
platform_scores
object
Per-platform scores (0-100 each)
curl -X POST https://searchcompany-main.up.railway.app/api/cron/store-visibility-score \
  -H "Content-Type: application/json" \
  -H "X-API-Key: search-company" \
  -d '{
    "business_id": "nike",
    "results": [
      {
        "prompt": "What are the best running shoes?",
        "chatgpt": true,
        "claude": true,
        "gemini": false,
        "perplexity": true,
        "copilot": false,
        "deepseek": true,
        "grok": false,
        "google_ai": true
      }
    ]
  }'
{
  "status": "success",
  "message": "Visibility score 48 stored for entity abc-123",
  "overall_score": 48,
  "prompts_analyzed": 10,
  "total_true": 38,
  "platform_scores": {
    "chatgpt_score": 52,
    "claude_score": 48,
    "gemini_score": 51,
    "perplexity_score": 45,
    "copilot_score": 49,
    "deepseek_score": 47,
    "grok_score": 46,
    "google_ai_score": 50
  }
}

Database Updates

entity_prompts_tracker

Updates prompts with their visibility results across 8 platforms (pass/fail per platform).

visibility_score_history

UPSERT single row per entity:
  • Updates recent_score with latest score
  • Appends new entry to history_scores JSONB array
  • History format: [{"date": "2025-12-15", "score": 41, "platforms": {...}}]
  • Keeps last 365 days of history

History Seeding

On first score calculation, the system seeds 2 days of fake history:
  • Day -2: Score 0 (baseline)
  • Day -1: Score 35 (simulated signup day)
This provides visual context for the visibility chart.

Notes

  • Called by Cron ONCE per entity after sampled prompts are analyzed
  • Each platform uses native search + Gemini 3 Flash provides unified evaluation
  • JSONB history enables the β€œVisibility Over Time” chart on the dashboard
  • Single row per entity = faster queries
  • Floor protection ensures scores trend upward over time