Les outils de suivi de classement professionnels facturent de 50 à 200 $/mois pour surveiller une poignée de mots-clés. Avec l’API Scavio Search à 0,005 $/crédit, vous pouvez suivre 50 mots-clés par jour pour 0,035 $/semaine — soit moins de 2 $/an. Ce tutoriel construit un tracker de classement complet avec stockage SQLite, planification cron quotidienne et alertes de changement de position.
Prérequis
- Python 3.11+
- Une clé API Scavio depuis https://scavio.dev
- SQLite3 (inclus avec Python)
- Un planificateur cron ou le Planificateur de tâches pour des exécutions quotidiennes
Parcours
Étape 1: Configurer la base de données SQLite et la liste de mots-clés
Créez une base de données SQLite pour stocker les instantanés quotidiens des classements. Chaque ligne enregistre le mot-clé, la position du domaine cible, la date et l’URL classée.
import sqlite3
from pathlib import Path
DB_PATH = Path("rank_tracker.db")
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS rankings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
position INTEGER,
url TEXT,
checked_at TEXT NOT NULL,
domain TEXT NOT NULL
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_keyword_date
ON rankings(keyword, checked_at)
""")
conn.commit()
conn.close()
KEYWORDS = [
"best API for web scraping 2026",
"cheap search API for agents",
"MCP server search tool",
# ... add up to 50 keywords
]
TARGET_DOMAIN = "scavio.dev"
init_db()Étape 2: Vérifier les classements via l’API Scavio Search
Pour chaque mot-clé, effectuez une recherche et trouvez la position de votre domaine cible dans les résultats. Stockez la position (ou null si non trouvé dans les premiers résultats) dans la base de données.
import httpx
from datetime import date
SCAVIO_API_KEY = "your-api-key"
async def check_ranking(client: httpx.AsyncClient, keyword: str) -> dict:
resp = await client.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_API_KEY},
json={"query": keyword, "num_results": 10}
)
resp.raise_for_status()
results = resp.json().get("results", [])
for i, r in enumerate(results):
url = r.get("url", "")
if TARGET_DOMAIN in url:
return {"position": i + 1, "url": url}
return {"position": None, "url": None}
async def check_all_keywords():
conn = sqlite3.connect(DB_PATH)
today = date.today().isoformat()
async with httpx.AsyncClient(timeout=15) as client:
for keyword in KEYWORDS:
result = await check_ranking(client, keyword)
conn.execute(
"INSERT INTO rankings (keyword, position, url, checked_at, domain) VALUES (?, ?, ?, ?, ?)",
(keyword, result["position"], result["url"], today, TARGET_DOMAIN)
)
conn.commit()
conn.close()Étape 3: Détecter les changements de position et générer des alertes
Comparez les classements d’aujourd’hui avec ceux d’hier. Signalez tout mot-clé qui a bougé de 3 positions ou plus dans un sens ou dans l’autre.
def get_position_changes(threshold: int = 3) -> list[dict]:
conn = sqlite3.connect(DB_PATH)
rows = conn.execute("""
SELECT
r1.keyword,
r2.position AS prev_position,
r1.position AS curr_position,
r1.url,
r1.checked_at
FROM rankings r1
JOIN rankings r2
ON r1.keyword = r2.keyword
AND r1.domain = r2.domain
AND r2.checked_at = date(r1.checked_at, '-1 day')
WHERE r1.checked_at = date('now')
AND r1.position IS NOT NULL
AND r2.position IS NOT NULL
AND ABS(r1.position - r2.position) >= ?
ORDER BY ABS(r1.position - r2.position) DESC
""", (threshold,)).fetchall()
conn.close()
changes = []
for keyword, prev, curr, url, checked in rows:
direction = "up" if curr < prev else "down"
changes.append({
"keyword": keyword,
"previous": prev,
"current": curr,
"change": prev - curr,
"direction": direction,
"url": url
})
return changesÉtape 4: Générer un rapport quotidien de coût et de résumé
Affichez un résumé montrant le nombre total de mots-clés suivis, les crédits utilisés, le coût et les changements de position significatifs. Planifiez ce script avec cron pour une automatisation quotidienne.
import asyncio
async def daily_report():
await check_all_keywords()
credits_used = len(KEYWORDS) # 1 credit per search
cost = credits_used * 0.005
weekly_cost = cost * 7
changes = get_position_changes(threshold=3)
print(f"Rank Tracker Report - {date.today().isoformat()}")
print(f"Keywords tracked: {len(KEYWORDS)}")
print(f"Credits used: {credits_used}")
print(f"Daily cost: {cost:.3f}")
print(f"Weekly cost: {weekly_cost:.3f}")
print(f"Position changes (3+): {len(changes)}")
for c in changes:
arrow = "improved" if c["direction"] == "up" else "dropped"
print(f" {c['keyword']}: #{c['previous']} -> #{c['current']} ({arrow} by {abs(c['change'])})")
asyncio.run(daily_report())
# Cron: 0 8 * * * cd /path/to/tracker && python rank_tracker.pyExemple Python
import asyncio
import sqlite3
import httpx
from datetime import date
from pathlib import Path
SCAVIO_API_KEY = "your-api-key"
TARGET_DOMAIN = "scavio.dev"
DB_PATH = Path("rank_tracker.db")
KEYWORDS = [
"best API for web scraping 2026",
"cheap search API for agents",
"MCP server search tool",
]
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS rankings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT, position INTEGER, url TEXT,
checked_at TEXT, domain TEXT
)
""")
conn.commit()
conn.close()
async def check_ranking(client, keyword):
resp = await client.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCAVIO_API_KEY},
json={"query": keyword, "num_results": 10}
)
resp.raise_for_status()
for i, r in enumerate(resp.json().get("results", [])):
if TARGET_DOMAIN in r.get("url", ""):
return i + 1, r["url"]
return None, None
async def main():
init_db()
conn = sqlite3.connect(DB_PATH)
today = date.today().isoformat()
async with httpx.AsyncClient(timeout=15) as client:
for kw in KEYWORDS:
pos, url = await check_ranking(client, kw)
conn.execute(
"INSERT INTO rankings VALUES (NULL,?,?,?,?,?)",
(kw, pos, url, today, TARGET_DOMAIN)
)
conn.commit()
cost = len(KEYWORDS) * 0.005
print(f"Tracked {len(KEYWORDS)} keywords | Cost: {cost:.3f} | Weekly: {cost * 7:.3f}")
conn.close()
asyncio.run(main())Exemple JavaScript
const SCAVIO_API_KEY = "your-api-key";
const TARGET_DOMAIN = "scavio.dev";
const KEYWORDS = [
"best API for web scraping 2026",
"cheap search API for agents",
"MCP server search tool"
];
async function checkRanking(keyword) {
const resp = await fetch("https://api.scavio.dev/api/v1/search", {
method: "POST",
headers: { "x-api-key": SCAVIO_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query: keyword, num_results: 10 })
});
const data = await resp.json();
const results = data.results || [];
for (let i = 0; i < results.length; i++) {
if ((results[i].url || "").includes(TARGET_DOMAIN)) {
return { position: i + 1, url: results[i].url };
}
}
return { position: null, url: null };
}
async function main() {
const rankings = [];
for (const kw of KEYWORDS) {
const rank = await checkRanking(kw);
rankings.push({ keyword: kw, ...rank });
}
const cost = KEYWORDS.length * 0.005;
console.log("Rankings:", JSON.stringify(rankings, null, 2));
console.log("Daily cost: $" + cost.toFixed(3));
console.log("Weekly cost: $" + (cost * 7).toFixed(3));
}
main();Sortie attendue
Tracked 3 keywords | Cost: $0.015 | Weekly: $0.105