La plupart des trackers de classement sont construits sur des scrapers fragiles qui tombent en panne chaque fois que Google modifie son HTML. En utilisant l'API Scavio comme couche de données, vous pouvez construire un tracker de classement qui renvoie des données de position structurées sans gérer de proxies ni de parseurs. Ce tutoriel construit une API Flask légère qui accepte des entrées de mot-clé et de domaine, vérifie les positions Google via Scavio, stocke les résultats dans SQLite et expose les données historiques de classement via un point de terminaison REST.
Prérequis
- Python 3.10 ou supérieur
- pip install requests flask
- Une clé API Scavio
- Compréhension de base de la conception d'API REST
Parcours
Étape 1: Configurer la base de données SQLite
Créer une table simple pour stocker le mot-clé, le domaine, la position et l'horodatage pour chaque vérification.
import sqlite3
def init_db(path: str = "rankings.db") -> None:
conn = sqlite3.connect(path)
conn.execute("""
CREATE TABLE IF NOT EXISTS rankings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL,
domain TEXT NOT NULL,
position INTEGER,
checked_at TEXT DEFAULT (datetime('now'))
)
""")
conn.commit()
conn.close()Étape 2: Vérifier le classement via Scavio
Interroger Google via l'API Scavio et analyser les résultats pour le domaine cible. Renvoyer la position ou None.
import requests
def check_rank(keyword: str, domain: str) -> int | None:
r = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={"query": keyword, "country_code": "us"}
)
r.raise_for_status()
for result in r.json().get("organic_results", []):
if domain in result.get("link", ""):
return result["position"]
return NoneÉtape 3: Stocker et récupérer les classements
Enregistrer chaque vérification de classement dans SQLite et fournir une fonction pour interroger les données historiques.
def save_rank(keyword: str, domain: str, position: int | None) -> None:
conn = sqlite3.connect("rankings.db")
conn.execute("INSERT INTO rankings (keyword, domain, position) VALUES (?, ?, ?)",
(keyword, domain, position))
conn.commit()
conn.close()
def get_history(keyword: str, domain: str, days: int = 30) -> list[dict]:
conn = sqlite3.connect("rankings.db")
rows = conn.execute(
"SELECT position, checked_at FROM rankings WHERE keyword=? AND domain=? ORDER BY checked_at DESC LIMIT ?",
(keyword, domain, days)
).fetchall()
conn.close()
return [{"position": r[0], "date": r[1]} for r in rows]Étape 4: Exposer en tant qu'API Flask
Créer deux points de terminaison : POST /check pour déclencher une vérification de classement, et GET /history pour récupérer les positions stockées.
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/check", methods=["POST"])
def check():
data = request.json
pos = check_rank(data["keyword"], data["domain"])
save_rank(data["keyword"], data["domain"], pos)
return jsonify({"keyword": data["keyword"], "domain": data["domain"], "position": pos})
@app.route("/history")
def history():
kw = request.args["keyword"]
domain = request.args["domain"]
return jsonify(get_history(kw, domain))Exemple Python
import os
import sqlite3
import requests
from flask import Flask, request, jsonify
API_KEY = os.environ.get("SCAVIO_API_KEY", "your_scavio_api_key")
ENDPOINT = "https://api.scavio.dev/api/v1/search"
DB = "rankings.db"
def init_db():
conn = sqlite3.connect(DB)
conn.execute("CREATE TABLE IF NOT EXISTS rankings (id INTEGER PRIMARY KEY AUTOINCREMENT, keyword TEXT, domain TEXT, position INTEGER, checked_at TEXT DEFAULT (datetime('now')))")
conn.commit()
conn.close()
def check_rank(keyword: str, domain: str) -> int | None:
r = requests.post(ENDPOINT, headers={"x-api-key": API_KEY},
json={"query": keyword, "country_code": "us"})
r.raise_for_status()
for res in r.json().get("organic_results", []):
if domain in res.get("link", ""):
return res["position"]
return None
app = Flask(__name__)
@app.route("/check", methods=["POST"])
def check():
d = request.json
pos = check_rank(d["keyword"], d["domain"])
conn = sqlite3.connect(DB)
conn.execute("INSERT INTO rankings (keyword, domain, position) VALUES (?, ?, ?)", (d["keyword"], d["domain"], pos))
conn.commit()
conn.close()
return jsonify({"keyword": d["keyword"], "position": pos})
if __name__ == "__main__":
init_db()
app.run(port=5000)Exemple JavaScript
const API_KEY = process.env.SCAVIO_API_KEY || "your_scavio_api_key";
const ENDPOINT = "https://api.scavio.dev/api/v1/search";
async function checkRank(keyword, domain) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ query: keyword, country_code: "us" })
});
const data = await res.json();
const match = (data.organic_results || []).find(r => r.link.includes(domain));
return match ? match.position : null;
}
async function main() {
const keywords = ["python api tutorial", "rest api guide"];
const domain = "mysite.com";
for (const kw of keywords) {
const pos = await checkRank(kw, domain);
console.log(`${kw}: ${pos ? "#" + pos : "not ranked"}`);
}
}
main().catch(console.error);Sortie attendue
POST /check {"keyword": "python api tutorial", "domain": "mysite.com"}
=> {"keyword": "python api tutorial", "position": 6}
GET /history?keyword=python+api+tutorial&domain=mysite.com
=> [{"position": 6, "date": "2026-04-19"}, {"position": 7, "date": "2026-04-18"}]