ScavioScavio
ProductPricingDocs
Sign InGet Started
  1. Home
  2. Tutorials
  3. How to Build a Rank Tracker on Cloudflare Workers
Tutorial

How to Build a Rank Tracker on Cloudflare Workers

Build a serverless rank tracker using Cloudflare Workers and Scavio API. Daily keyword tracking with zero infrastructure.

Get Free API KeyAPI Docs

Rank trackers need to run daily but do not justify a full server. This tutorial builds a rank tracker on Cloudflare Workers that runs on a cron schedule, checks your keyword positions via the Scavio API, stores results in KV, and exposes a dashboard endpoint. Zero infrastructure, free Workers tier, $0.005/keyword.

Prerequisites

  • Node.js 18+
  • Wrangler CLI (npm install -g wrangler)
  • A Scavio API key from scavio.dev
  • Cloudflare account (free tier works)

Walkthrough

Step 1: Set up the Worker with scheduled trigger

Create a Cloudflare Worker that runs daily via cron trigger.

JavaScript
// wrangler.toml config:
// name = "rank-tracker"
// [triggers]
// crons = ["0 6 * * *"]  # Daily at 6 AM UTC
// [[kv_namespaces]]
// binding = "RANKINGS"
// id = "your-kv-namespace-id"

export default {
  async scheduled(event, env, ctx) {
    const keywords = ['search api python', 'mcp search tool', 'serp api alternative', 'web search api pricing'];
    const domain = 'scavio.dev';
    const results = [];
    for (const kw of keywords) {
      const resp = await fetch('https://api.scavio.dev/api/v1/search', {
        method: 'POST',
        headers: { 'x-api-key': env.SCAVIO_API_KEY, 'Content-Type': 'application/json' },
        body: JSON.stringify({ query: kw, country_code: 'us' }),
      });
      const data = await resp.json();
      const organic = data.organic_results || [];
      const pos = organic.findIndex(r => r.link?.includes(domain));
      results.push({ keyword: kw, position: pos >= 0 ? pos + 1 : null, date: new Date().toISOString().split('T')[0] });
    }
    // Store in KV
    const today = new Date().toISOString().split('T')[0];
    await env.RANKINGS.put(`rankings:${today}`, JSON.stringify(results));
    console.log(`Tracked ${results.length} keywords. Cost: $${(keywords.length * 0.005).toFixed(3)}`);
  },

  async fetch(request, env) {
    // Dashboard endpoint
    const url = new URL(request.url);
    if (url.pathname === '/api/rankings') {
      const today = new Date().toISOString().split('T')[0];
      const data = await env.RANKINGS.get(`rankings:${today}`);
      return new Response(data || '[]', { headers: { 'Content-Type': 'application/json' } });
    }
    return new Response('Rank Tracker. GET /api/rankings for today.');
  }
};

Step 2: Add historical trend storage

Store 30 days of ranking history and calculate trends.

JavaScript
async function getHistory(env, days = 30) {
  const history = [];
  for (let i = 0; i < days; i++) {
    const date = new Date(Date.now() - i * 86400000).toISOString().split('T')[0];
    const data = await env.RANKINGS.get(`rankings:${date}`);
    if (data) {
      history.push({ date, rankings: JSON.parse(data) });
    }
  }
  return history.reverse();
}

async function getTrends(env, keyword) {
  const history = await getHistory(env);
  const positions = [];
  for (const day of history) {
    const entry = day.rankings.find(r => r.keyword === keyword);
    if (entry) {
      positions.push({ date: day.date, position: entry.position });
    }
  }
  if (positions.length < 2) return { trend: 'insufficient_data', positions };
  const latest = positions[positions.length - 1].position;
  const previous = positions[positions.length - 2].position;
  let trend = 'stable';
  if (latest && previous) {
    if (latest < previous) trend = 'improving';
    else if (latest > previous) trend = 'declining';
  } else if (latest && !previous) {
    trend = 'new_ranking';
  } else if (!latest && previous) {
    trend = 'lost_ranking';
  }
  return { trend, positions, latest, previous };
}

// Usage in fetch handler:
// const trends = await getTrends(env, 'search api python');
// return new Response(JSON.stringify(trends));
console.log('Trend tracking configured with 30-day KV history');

Step 3: Deploy and verify

Deploy the Worker and test the ranking endpoint.

JavaScript
// Deploy steps:
// 1. wrangler secret put SCAVIO_API_KEY
// 2. wrangler deploy
// 3. wrangler kv:namespace create RANKINGS

// Test locally:
// wrangler dev
// curl http://localhost:8787/api/rankings

// Test the scheduled trigger:
// wrangler dev --test-scheduled

// Verify in production:
async function verifyDeployment() {
  const resp = await fetch('https://rank-tracker.your-subdomain.workers.dev/api/rankings');
  const data = await resp.json();
  console.log(`Rankings for today: ${data.length} keywords`);
  for (const r of data) {
    const pos = r.position ? `#${r.position}` : 'not found';
    console.log(`  ${r.keyword.padEnd(30)} | ${pos}`);
  }
}

// Cost breakdown:
// Cloudflare Workers: Free tier (100K req/day)
// Scavio API: 4 keywords x $0.005 = $0.020/day
// Monthly: $0.60/month for daily tracking
// vs. Ahrefs: $99/mo, SEMrush: $129/mo
console.log('Deployed. $0.020/day for 4 keywords.');
console.log('Monthly: $0.60 vs Ahrefs $99/mo');

Python Example

Python
import os, requests
SH = {'x-api-key': os.environ['SCAVIO_API_KEY'], 'Content-Type': 'application/json'}

def track(keyword, domain):
    data = requests.post('https://api.scavio.dev/api/v1/search',
        headers=SH, json={'query': keyword, 'country_code': 'us'}, timeout=10).json()
    pos = next((i+1 for i, r in enumerate(data.get('organic_results', [])) if domain in r.get('link', '')), None)
    print(f'{keyword[:30]:30} | {f"#{pos}" if pos else "absent"}')

for kw in ['search api python', 'mcp search tool']:
    track(kw, 'scavio.dev')

JavaScript Example

JavaScript
const SH = { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' };
for (const kw of ['search api python', 'mcp search tool']) {
  const data = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST', headers: SH,
    body: JSON.stringify({ query: kw, country_code: 'us' })
  }).then(r => r.json());
  const pos = (data.organic_results || []).findIndex(r => r.link?.includes('scavio.dev'));
  console.log(`${kw}: ${pos >= 0 ? '#' + (pos+1) : 'absent'}`);
}

Expected Output

JSON
Tracked 4 keywords. Cost: $0.020

Rankings for today: 4 keywords
  search api python              | #3
  mcp search tool                | #2
  serp api alternative           | #5
  web search api pricing         | #4

Deployed. $0.020/day for 4 keywords.
Monthly: $0.60 vs Ahrefs $99/mo

Related Tutorials

  • How to Use Scavio as Data Source for OpenSEO
  • How to Build an SEO Dashboard with Retool and Scavio
  • How to Build a DIY Keyword Rank Tracker

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Node.js 18+. Wrangler CLI (npm install -g wrangler). A Scavio API key from scavio.dev. Cloudflare account (free tier works). A Scavio API key gives you 50 free credits on signup.

Yes. The free tier includes 50 credits on signup, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Related Resources

Best Of

Best SEO Data Sources for Cloudflare Workers (2026)

Read more
Best Of

Best API-Based Rank Trackers for SEO in 2026

Read more
Solution

Run SEO Rank Checks Overnight in Batch Mode

Read more
Solution

Build Reliable Local Rank Tracking with Scavio API

Read more
Comparison

Search APIs (Scavio, Tavily, SerpAPI) vs Headless Browser (Playwright, Puppeteer, Browserbase)

Read more
Glossary

Search API Provider Landscape (2026)

Read more

Start Building

Build a serverless rank tracker using Cloudflare Workers and Scavio API. Daily keyword tracking with zero infrastructure.

Get Free API KeyRead the Docs
ScavioScavio

Real-time search API for AI agents. Search every platform, not just Google.

Product

  • Features
  • Pricing
  • Dashboard
  • Affiliates

Developers

  • Documentation
  • API Reference
  • Quickstart
  • MCP Integration
  • Python SDK

Alternatives

  • Tavily Alternative
  • SerpAPI Alternative
  • Firecrawl Alternative
  • Exa Alternative

Tools

  • JSON Formatter
  • cURL to Code
  • Token Counter
  • All Tools

© 2026 Scavio. All rights reserved.

Featured on TAAFT
Terms of ServicePrivacy Policy