Les outils SEO d'entreprise facturent 200 à 500 $/mois pour des tableaux de bord que vous ne pouvez pas personnaliser. Une API SERP alimentant votre propre tableau de bord vous donne exactement les mesures dont vous avez besoin pour une fraction du coût. Ce tutoriel construit un backend de suivi de classement en Python qui vérifie les positions de mots-clés quotidiennement, stocke l'historique dans SQLite et exporte des CSV pour n'importe quel frontend. Le suivi de 200 mots-clés par jour coûte environ 1 $/jour sur Scavio.
Prérequis
- Python 3.8+ installé
- requests et sqlite3 (intégrés)
- Une clé API Scavio depuis scavio.dev
- Mots-clés et votre domaine cible
Parcours
Étape 1: Configurer la base de données et la configuration
Créez une base de données SQLite pour les instantanés de classement quotidiens. Définissez votre domaine cible et votre liste de mots-clés.
import os, sqlite3, requests, json
from datetime import date
API_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
DOMAIN = 'mysite.com'
KEYWORDS = ['serp api python', 'web scraping api', 'tiktok data api', 'seo rank tracking api']
def init_db(path='seo_dashboard.db'):
conn = sqlite3.connect(path)
conn.execute('''CREATE TABLE IF NOT EXISTS rankings (
id INTEGER PRIMARY KEY, date TEXT, keyword TEXT,
position INTEGER, url TEXT, title TEXT)''')
conn.commit()
return conn
db = init_db()Étape 2: Récupérez les classements pour chaque mot-clé
Interrogez Google pour chaque mot-clé et trouvez la position de votre domaine dans les résultats organiques.
def check_keyword(keyword):
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers=H, json={'query': keyword, 'country_code': 'us'})
for r in resp.json().get('organic_results', []):
if DOMAIN in r.get('link', ''):
return {'position': r['position'], 'url': r['link'], 'title': r.get('title', '')}
return {'position': None, 'url': '', 'title': ''}
def run_daily_check():
today = date.today().isoformat()
for kw in KEYWORDS:
r = check_keyword(kw)
db.execute('INSERT INTO rankings (date,keyword,position,url,title) VALUES (?,?,?,?,?)',
(today, kw, r['position'], r['url'], r['title']))
status = f"#{r['position']}" if r['position'] else 'NOT FOUND'
print(f' {kw}: {status}')
db.commit()
print(f'Cost: ${len(KEYWORDS) * 0.005:.3f}')
run_daily_check()Étape 3: Interrogez l'historique et détectez les changements de classement
Extrayez l'historique des classements de SQLite pour identifier les tendances, les baisses et les améliorations.
def get_movers(days=7):
cursor = db.execute('''
WITH recent AS (
SELECT keyword, position, date,
ROW_NUMBER() OVER (PARTITION BY keyword ORDER BY date DESC) as rn
FROM rankings WHERE position IS NOT NULL
)
SELECT a.keyword, a.position as current_pos, b.position as prev_pos,
b.position - a.position as change
FROM recent a JOIN recent b ON a.keyword = b.keyword
WHERE a.rn = 1 AND b.rn = ? AND a.position != b.position
ORDER BY change DESC
''', (min(days, 30),))
for kw, curr, prev, change in cursor.fetchall():
d = 'UP' if change > 0 else 'DOWN'
print(f' [{d}] {kw}: #{prev} -> #{curr} ({change:+d})')
def export_csv(output='dashboard.csv'):
import csv
rows = db.execute('SELECT date, keyword, position, url FROM rankings ORDER BY date DESC, keyword').fetchall()
with open(output, 'w', newline='') as f:
w = csv.writer(f)
w.writerow(['date', 'keyword', 'position', 'url'])
w.writerows(rows)
print(f'Exported {len(rows)} rows to {output}')
get_movers(7)
export_csv()Étape 4: Planifiez des vérifications quotidiennes
Ajoutez une tâche cron pour exécuter le vérificateur quotidiennement avec une prévention des doublons.
def already_checked_today():
today = date.today().isoformat()
return db.execute('SELECT COUNT(*) FROM rankings WHERE date = ?', (today,)).fetchone()[0] > 0
if __name__ == '__main__':
db = init_db()
if already_checked_today():
print('Already checked today.')
else:
print(f'{date.today()}: Running daily check...')
run_daily_check()
get_movers(7)
# crontab -e
# 0 6 * * * cd /path/to && python seo_dashboard.py >> dashboard.log 2>&1Exemple Python
import os, sqlite3, requests
from datetime import date
API_KEY = os.environ['SCAVIO_API_KEY']
H = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
DOMAIN = 'mysite.com'
KEYWORDS = ['serp api python', 'web scraping api']
db = sqlite3.connect('seo.db')
db.execute('CREATE TABLE IF NOT EXISTS ranks (date TEXT, keyword TEXT, position INT)')
for kw in KEYWORDS:
data = requests.post('https://api.scavio.dev/api/v1/search',
headers=H, json={'query': kw, 'country_code': 'us'}).json()
pos = next((r['position'] for r in data.get('organic_results', []) if DOMAIN in r.get('link', '')), None)
db.execute('INSERT INTO ranks VALUES (?,?,?)', (date.today().isoformat(), kw, pos))
print(f'{kw}: #{pos}' if pos else f'{kw}: not found')
db.commit()Exemple JavaScript
const Database = require('better-sqlite3');
const API_KEY = process.env.SCAVIO_API_KEY;
const H = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };
const DOMAIN = 'mysite.com';
const KEYWORDS = ['serp api python', 'web scraping api'];
const db = new Database('seo.db');
db.exec('CREATE TABLE IF NOT EXISTS ranks (date TEXT, keyword TEXT, position INT)');
async function run() {
const today = new Date().toISOString().slice(0, 10);
const insert = db.prepare('INSERT INTO ranks VALUES (?, ?, ?)');
for (const kw of KEYWORDS) {
const data = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: H, body: JSON.stringify({ query: kw, country_code: 'us' })
}).then(r => r.json());
const match = (data.organic_results || []).find(r => r.link.includes(DOMAIN));
insert.run(today, kw, match ? match.position : null);
console.log(match ? `${kw}: #${match.position}` : `${kw}: not found`);
}
}
run().catch(console.error);Sortie attendue
2026-05-18: Running daily check...
serp api python: #4
web scraping api: #7
tiktok data api: #2
seo rank tracking api: #11
Cost: $0.020
[UP] tiktok data api: #5 -> #2 (+3)
[DOWN] seo rank tracking api: #8 -> #11 (-3)
Exported 28 rows to dashboard.csv