📍 Semrush - Position Tracking
Overview
Monitor keyword rankings over time with historical position data, ranking distribution summaries, and URL-level tracking. Includes subscription-based limits and intelligent sorting.
Position Tracking
Get Keyword Positions
GET /keyword/tracking/:domain
Purpose: Track keyword rankings with historical data and ranking distribution
Request:
GET /v1/integrations/semrush/keyword/tracking/example.com?config_id=507f1f77bcf86cd799439011&limit=25&page=1&sortBy=current_rank&sortOrder=asc&new=false
Authorization: Bearer {jwt_token}
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
config_id | String | Required | Project configuration ID |
limit | Integer | 25 | Keywords per page (1-100) |
page | Integer | 1 | Page number |
sortBy | String | current_rank | Sort field (see below) |
sortOrder | String | asc | asc or desc |
new | Boolean | false | Force refresh from API |
Sort Fields:
current_rank- Current SERP positionstart_rank- Initial position when tracking starteddifference- Position change (start vs current)keyword- Alphabetical
Subscription Required: Active SEO subscription (Pro/Plus/Platinum)
Semrush API:
GET https://api.semrush.com/reports/v1/projects/{CAMPAIGN_ID}/tracking?key={API_KEY}&action=report&type=tracking_position_organic&display_sort=0_pos_asc&url={FORMATTED_URL}&display_limit=200
URL Formatting:
- Main domain:
*.example.com/* - Subdomain:
sub.example.com/*
JSON Response:
{
"success": true,
"message": "SUCCESS",
"data": {
"data": {
"project_name": "Client Website SEO",
"project_domain": "example.com",
"keywords": [
{
"keyword": "quality products",
"start_date": "2025-01-15",
"start_rank": 15,
"current_rank": 8,
"difference": -7,
"url": "https://example.com/products"
},
{
"keyword": "best deals online",
"start_date": "2025-01-15",
"start_rank": 25,
"current_rank": 3,
"difference": -22,
"url": "https://example.com/deals"
},
{
"keyword": "free shipping",
"start_date": "2025-01-20",
"start_rank": 12,
"current_rank": 1,
"difference": -11,
"url": "https://example.com/shipping"
}
],
"keyword_summary": {
"rank_1": 1,
"rank_2_5": 2,
"rank_6_10": 5,
"rank_10+": 12
}
},
"pagination": {
"currentPage": 1,
"totalPages": 2,
"totalItems": 20,
"itemsPerPage": 25,
"hasNextPage": false,
"hasPreviousPage": false
},
"totalCount": 20,
"createdAt": "2025-10-10T08:00:00Z",
"lastUpdate": "2025-10-10T08:00:00Z",
"nextUpdate": "2025-10-17T08:00:00Z",
"daysSinceUpdate": 0,
"daysUntilUpdate": 7,
"isStale": false
}
}
Keyword Fields
| Field | Type | Description |
|---|---|---|
| keyword | String | Search query being tracked |
| start_date | Date | When tracking began (YYYY-MM-DD) |
| start_rank | Integer | Initial position |
| current_rank | Integer | Current position |
| difference | Integer | Position change (negative = improvement) |
| url | String | Page ranking for keyword |
Keyword Summary
Ranking Distribution
Automatically calculated based on current positions:
| Category | Description | Count |
|---|---|---|
| rank_1 | Position 1 (top spot) | Integer |
| rank_2_5 | Positions 2-5 (page 1 top) | Integer |
| rank_6_10 | Positions 6-10 (page 1 bottom) | Integer |
| rank_10+ | Beyond position 10 (page 2+) | Integer |
Use Case: Quickly assess ranking performance
Subscription Plans
Keyword Limits
| Plan | Keyword Limit | Access Level |
|---|---|---|
| Pro | 10 keywords | Entry-level tracking |
| Plus | 20 keywords | Standard tracking |
| Platinum | 40 keywords | Advanced tracking |
| Internal | Unlimited | DashClicks staff only |
Limit Enforcement
- Pagination respects plan limits
totalCountcapped at plan limit for external users- Keyword summary calculated on limited dataset
- Sorting applied before limiting
Example (Pro plan, 10 keyword limit):
// Request with limit=25, but only 10 returned
{
"pagination": {
"totalItems": 10, // Capped at plan limit
"itemsPerPage": 25
},
"totalCount": 10
}
Caching Strategy
Cache Configuration:
- Cache Type:
keyword_position_tracking_organic - TTL: 7 days (weekly data)
- Force Refresh:
?new=true
MongoDB Aggregation:
// Efficient sorting and pagination
getSortedKeywordData({
accountID,
domain,
sortBy: 'current_rank',
sortOrder: 'asc',
limit: 25,
skip: 0,
maxKeywords: 20, // Plan limit
isPlanLimitedUser: true,
});
Use Cases
1. Weekly Ranking Report
Track progress over time:
// Force weekly refresh
GET /keyword/tracking/client.com?config_id={id}&new=true
// Calculate improvements
keywords.filter(k => k.difference < 0).length; // Improved keywords
keywords.filter(k => k.difference > 0).length; // Declined keywords
2. Top Rankings Dashboard
Show best-performing keywords:
GET /keyword/tracking/client.com?config_id={id}&sortBy=current_rank&sortOrder=asc&limit=10
// Display top 10 ranking keywords
3. Progress Monitoring
Track position changes:
keywords.forEach(k => {
const change = Math.abs(k.difference);
const direction = k.difference < 0 ? 'improved' : 'declined';
console.log(`${k.keyword}: ${direction} by ${change} positions`);
});
4. Ranking Distribution Analysis
Visualize performance:
const summary = response.data.data.keyword_summary;
const total = summary.rank_1 + summary.rank_2_5 + summary.rank_6_10 + summary['rank_10+'];
console.log(`Top 1: ${((summary.rank_1 / total) * 100).toFixed(1)}%`);
console.log(`Top 5: ${(((summary.rank_1 + summary.rank_2_5) / total) * 100).toFixed(1)}%`);
console.log(
`Page 1: ${(((summary.rank_1 + summary.rank_2_5 + summary.rank_6_10) / total) * 100).toFixed(
1,
)}%`,
);
5. URL Performance
Track which pages rank:
const urlPerformance = {};
keywords.forEach(k => {
if (!urlPerformance[k.url]) {
urlPerformance[k.url] = { keywords: 0, avgRank: 0 };
}
urlPerformance[k.url].keywords++;
urlPerformance[k.url].avgRank += k.current_rank;
});
// Calculate averages
Object.keys(urlPerformance).forEach(url => {
urlPerformance[url].avgRank /= urlPerformance[url].keywords;
});
Error Handling
Missing Configuration
{
"success": false,
"message": "Invalid project configuration provided",
"statusCode": 400
}
Solution: Create project configuration first
No Subscription
{
"success": false,
"error_code": "SUBSCRIPTION_REQUIRED",
"message": "SEO subscription required to access this feature",
"statusCode": 403
}
Solution: Activate SEO subscription (Pro/Plus/Platinum)
Invalid Campaign
{
"success": false,
"message": "ERROR 40 :: Campaign not found",
"statusCode": 500
}
Solution: Verify campaign_id in project configuration
Best Practices
Refresh Frequency
- ⏰ Weekly: Standard refresh cycle (matches Semrush update)
- 🔄 Force Refresh: Use sparingly to avoid rate limits
- 📊 Cached Data: Acceptable for daily reports
Sorting Strategy
- Dashboard: Sort by
current_rankASC (show best first) - Progress Report: Sort by
differenceASC (biggest improvements) - Alphabetical: Sort by
keywordASC (organized lists)
Plan Management
- 🎯 Pro (10 keywords): Focus on highest-value keywords
- 📈 Plus (20 keywords): Mix of primary and secondary keywords
- 💎 Platinum (40 keywords): Comprehensive tracking