MENU navbar-image

Introduction

Ride the wave of search success with our gnarly SERP tracking API! 🏄‍

Welcome to the SERPs Up API! 🏄‍ This documentation will help you ride the waves of SERP data like a total pro.

Our API lets you programmatically access all your keyword rankings, project data, and team information. Perfect for building custom dashboards, automating reports, or integrating with your existing SEO tools.

<aside>As you scroll, you'll see gnarly code examples in different programming languages. Switch between languages using the tabs - it's totally tubular! 🌊</aside>

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {your_api_token}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

You can retrieve your API token by visiting your Settings page and clicking Generate API Token. 🌊

API Tokens

Manage your API access tokens. Generate tokens to access the SERPs Up API programmatically! 🔑

List API tokens

requires authentication

Get all your API tokens with usage information. Tokens are never shown again after creation!

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/tokens" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/tokens"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/tokens';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/tokens'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": [
        {
            "id": 1,
            "name": "My Dashboard Integration",
            "abilities": [
                "projects:read",
                "keywords:read"
            ],
            "last_used_at": "2024-01-20T08:00:00Z",
            "expires_at": "2024-12-31T23:59:59Z",
            "created_at": "2024-01-15T10:00:00Z"
        }
    ],
    "status": "success",
    "message": "API tokens retrieved successfully.",
    "code": 200
}
 

Request      

GET api/v1/tokens

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Create API token

requires authentication

Generate a new API token for programmatic access. Save the token - it won't be shown again!

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/tokens" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"\\\"My Dashboard Integration\\\"\",
    \"team_id\": 1,
    \"abilities\": [
        \"projects:read\",
        \"keywords:write\"
    ],
    \"expires_at\": \"\\\"2024-12-31T23:59:59Z\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/tokens"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "\"My Dashboard Integration\"",
    "team_id": 1,
    "abilities": [
        "projects:read",
        "keywords:write"
    ],
    "expires_at": "\"2024-12-31T23:59:59Z\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/tokens';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => '"My Dashboard Integration"',
            'team_id' => 1,
            'abilities' => [
                'projects:read',
                'keywords:write',
            ],
            'expires_at' => '"2024-12-31T23:59:59Z"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/tokens'
payload = {
    "name": "\"My Dashboard Integration\"",
    "team_id": 1,
    "abilities": [
        "projects:read",
        "keywords:write"
    ],
    "expires_at": "\"2024-12-31T23:59:59Z\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):


{
    "data": {
        "id": 1,
        "name": "My Dashboard Integration",
        "token": "1|abcdef123456789...",
        "abilities": [
            "projects:read",
            "keywords:write"
        ],
        "expires_at": "2024-12-31T23:59:59Z",
        "created_at": "2024-01-20T10:00:00Z"
    },
    "status": "success",
    "message": "API token created successfully.",
    "code": 201
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to create tokens for this team.",
    "code": 403
}
 

Request      

POST api/v1/tokens

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

name   string   

Token name for identification. Example: "My Dashboard Integration"

team_id   integer   

Team ID this token belongs to. Example: 1

abilities   string[]  optional  

Token permissions.

expires_at   string  optional  

Token expiration date (ISO 8601). Example: "2024-12-31T23:59:59Z"

Delete API token

requires authentication

Revoke an API token. This will immediately stop all access using this token.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/tokens/16" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/tokens/16"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/tokens/16';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/tokens/16'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": null,
    "status": "success",
    "message": "API token deleted successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to delete this token.",
    "code": 403
}
 

Request      

DELETE api/v1/tokens/{token_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

token_id   integer   

The ID of the token. Example: 16

token   integer   

The token ID to delete. Example: 1

Keywords

Manage keywords for your SERP tracking projects. Track those gnarly search terms! 🎯

List project keywords

requires authentication

Get all keywords for a specific project with their latest rankings.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/projects/1/keywords" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/projects/1/keywords"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/projects/1/keywords';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/projects/1/keywords'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": [
        {
            "id": 1,
            "keyword": "best seo tools",
            "priority": true,
            "active": true,
            "latest_ranking": {
                "position": 3,
                "checked_at": "2024-01-20T08:00:00Z"
            }
        }
    ],
    "status": "success",
    "message": "Keywords retrieved successfully.",
    "code": 200
}
 

Request      

GET api/v1/projects/{project_id}/keywords

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project. Example: 1

project   integer   

The project ID. Example: 1

Add keywords to project

requires authentication

Add one or more keywords to track for this project. Bulk creation supported!

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/projects/1/keywords" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"keywords\": [
        \"seo tools\",
        \"rank tracker\"
    ],
    \"priority\": true,
    \"devices\": [
        \"desktop\"
    ]
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/projects/1/keywords"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "keywords": [
        "seo tools",
        "rank tracker"
    ],
    "priority": true,
    "devices": [
        "desktop"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/projects/1/keywords';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'keywords' => [
                'seo tools',
                'rank tracker',
            ],
            'priority' => true,
            'devices' => [
                'desktop',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/projects/1/keywords'
payload = {
    "keywords": [
        "seo tools",
        "rank tracker"
    ],
    "priority": true,
    "devices": [
        "desktop"
    ]
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):


{
    "data": [
        {
            "id": 1,
            "keyword": "seo tools",
            "priority": true,
            "active": true,
            "created_at": "2024-01-20T10:00:00Z"
        }
    ],
    "status": "success",
    "message": "Keywords created successfully.",
    "code": 201
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to add keywords to this project.",
    "code": 403
}
 

Request      

POST api/v1/projects/{project_id}/keywords

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project. Example: 1

project   integer   

The project ID. Example: 1

Body Parameters

keywords   string[]   

Array of keywords to track.

priority   boolean  optional  

Whether these are priority keywords. Example: true

devices   string[]  optional  
Must be one of:
  • desktop
  • mobile
  • tablet

Get keyword details

requires authentication

Retrieve detailed information about a keyword including all ranking history.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/projects/1/keywords/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/projects/1/keywords/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/projects/1/keywords/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/projects/1/keywords/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "keyword": "best seo tools",
        "priority": true,
        "active": true,
        "rankings": [
            {
                "position": 3,
                "checked_at": "2024-01-20T08:00:00Z",
                "search_engine": "google"
            }
        ]
    },
    "status": "success",
    "message": "Keyword retrieved successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to access this keyword.",
    "code": 403
}
 

Request      

GET api/v1/projects/{project_id}/keywords/{keyword_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project. Example: 1

keyword_id   integer   

The ID of the keyword. Example: 1

project   integer   

The project ID. Example: 1

keyword   integer   

The keyword ID. Example: 1

Update keyword

requires authentication

Update keyword settings like priority status or pause/resume tracking.

Example request:
curl --request PATCH \
    "http://rank-tracker.test/api/v1/projects/1/keywords/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"keyword\": \"\\\"updated seo tools\\\"\",
    \"priority\": false,
    \"active\": true
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/projects/1/keywords/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "keyword": "\"updated seo tools\"",
    "priority": false,
    "active": true
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/projects/1/keywords/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'keyword' => '"updated seo tools"',
            'priority' => false,
            'active' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/projects/1/keywords/1'
payload = {
    "keyword": "\"updated seo tools\"",
    "priority": false,
    "active": true
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PATCH', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "keyword": "updated seo tools",
        "priority": false,
        "active": true,
        "updated_at": "2024-01-20T10:30:00Z"
    },
    "status": "success",
    "message": "Keyword updated successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to update this keyword.",
    "code": 403
}
 

Request      

PATCH api/v1/projects/{project_id}/keywords/{keyword_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project. Example: 1

keyword_id   integer   

The ID of the keyword. Example: 1

project   integer   

The project ID. Example: 1

keyword   integer   

The keyword ID. Example: 1

Body Parameters

keyword   string  optional  

The keyword text. Example: "updated seo tools"

priority   boolean  optional  

Mark as priority keyword. Example: false

active   boolean  optional  

Enable/disable tracking. Example: true

Delete keyword

requires authentication

Remove a keyword from tracking. This will also delete all ranking history.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/projects/1/keywords/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/projects/1/keywords/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/projects/1/keywords/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/projects/1/keywords/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": null,
    "status": "success",
    "message": "Keyword deleted successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to delete this keyword.",
    "code": 403
}
 

Request      

DELETE api/v1/projects/{project_id}/keywords/{keyword_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

project_id   integer   

The ID of the project. Example: 1

keyword_id   integer   

The ID of the keyword. Example: 1

project   integer   

The project ID. Example: 1

keyword   integer   

The keyword ID. Example: 1

Rankings

Track and manage SERP ranking data for your keywords. See how you're riding the waves! 📈

Get keyword rankings

requires authentication

Retrieve ranking history for a specific keyword, ordered by most recent first.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/keywords/1/rankings?page=1&per_page=20" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"per_page\": 1
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/keywords/1/rankings"
);

const params = {
    "page": "1",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "per_page": 1
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/keywords/1/rankings';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'page' => '1',
            'per_page' => '20',
        ],
        'json' => [
            'per_page' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/keywords/1/rankings'
payload = {
    "per_page": 1
}
params = {
  'page': '1',
  'per_page': '20',
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()

Example response (200):


{
    "data": {
        "rankings": [
            {
                "id": 1,
                "position": 3,
                "url": "https://example.com/page",
                "featured_snippet": false,
                "competitors": [
                    "competitor1.com",
                    "competitor2.com"
                ],
                "checked_at": "2024-01-20T08:00:00Z"
            }
        ],
        "meta": {
            "current_page": 1,
            "total": 150
        }
    },
    "status": "success",
    "message": "Rankings retrieved successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to access this keyword.",
    "code": 403
}
 

Example response (422):


{
    "data": null,
    "status": "error",
    "message": "The limit is up to 100 rankings per page. Please try again with a lower per page request.",
    "code": 422
}
 

Request      

GET api/v1/keywords/{keyword_id}/rankings

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

keyword_id   integer   

The ID of the keyword. Example: 1

keyword   integer   

The keyword ID. Example: 1

Query Parameters

page   integer  optional  

Page number for pagination. Example: 1

per_page   integer  optional  

Results per page (max 100). Example: 20

Body Parameters

per_page   integer  optional  

Must be at least 1. Must not be greater than 100. Example: 1

Record new ranking

requires authentication

Add a new ranking data point for a keyword. Used for manual updates or external integrations.

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/keywords/1/rankings" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"position\": 5,
    \"url\": \"\\\"https:\\/\\/example.com\\/seo-guide\\\"\",
    \"featured_snippet\": true,
    \"competitors\": [
        \"competitor1.com\",
        \"competitor2.com\"
    ],
    \"checked_at\": \"\\\"2024-01-20T08:00:00Z\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/keywords/1/rankings"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "position": 5,
    "url": "\"https:\/\/example.com\/seo-guide\"",
    "featured_snippet": true,
    "competitors": [
        "competitor1.com",
        "competitor2.com"
    ],
    "checked_at": "\"2024-01-20T08:00:00Z\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/keywords/1/rankings';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'position' => 5,
            'url' => '"https://example.com/seo-guide"',
            'featured_snippet' => true,
            'competitors' => [
                'competitor1.com',
                'competitor2.com',
            ],
            'checked_at' => '"2024-01-20T08:00:00Z"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/keywords/1/rankings'
payload = {
    "position": 5,
    "url": "\"https:\/\/example.com\/seo-guide\"",
    "featured_snippet": true,
    "competitors": [
        "competitor1.com",
        "competitor2.com"
    ],
    "checked_at": "\"2024-01-20T08:00:00Z\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):


{
    "data": {
        "id": 1,
        "position": 5,
        "url": "https://example.com/seo-guide",
        "featured_snippet": true,
        "competitors": [
            "competitor1.com",
            "competitor2.com"
        ],
        "checked_at": "2024-01-20T08:00:00Z",
        "created_at": "2024-01-20T08:05:00Z"
    },
    "status": "success",
    "message": "Ranking recorded successfully.",
    "code": 201
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to add rankings for this keyword.",
    "code": 403
}
 

Request      

POST api/v1/keywords/{keyword_id}/rankings

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

keyword_id   integer   

The ID of the keyword. Example: 1

keyword   integer   

The keyword ID. Example: 1

Body Parameters

position   integer  optional  

Ranking position (1-100, null if not found). Example: 5

url   string  optional  

The URL that ranked for this keyword. Example: "https://example.com/seo-guide"

featured_snippet   boolean  optional  

Whether this result had a featured snippet. Example: true

competitors   string[]  optional  

Array of competitor domains in top 10.

checked_at   string  optional  

When this ranking was checked (ISO 8601). Example: "2024-01-20T08:00:00Z"

Searches

Search locations and manage search results for SERP tracking. Find your target locations! 🌍

Search Locations

requires authentication

Get a paginated list of available search locations with filtering options.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/locations?country_code=US&target_type=City&search=New+York&per_page=25" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/locations"
);

const params = {
    "country_code": "US",
    "target_type": "City",
    "search": "New York",
    "per_page": "25",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/locations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'country_code' => 'US',
            'target_type' => 'City',
            'search' => 'New York',
            'per_page' => '25',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/locations'
params = {
  'country_code': 'US',
  'target_type': 'City',
  'search': 'New York',
  'per_page': '25',
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": {
        "locations": [
            {
                "id": 1,
                "name": "New York",
                "canonical_name": "New York,New York,United States",
                "country_code": "US",
                "target_type": "City"
            }
        ]
    },
    "links": {
        "first": "...",
        "last": "...",
        "prev": null,
        "next": "..."
    },
    "meta": {
        "current_page": 1,
        "per_page": 25,
        "total": 150
    },
    "status": "success",
    "message": "Locations retrieved successfully.",
    "code": 200
}
 

Request      

GET api/v1/locations

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

country_code   string  optional  

Filter by 2-letter country code. Example: US

target_type   string  optional  

Filter by location type. Example: City

search   string  optional  

Search locations by name. Example: New York

per_page   integer  optional  

Results per page (max 100). Example: 25

Get Location Details

requires authentication

Get detailed information about a specific location including parent/child relationships.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/locations/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/locations/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/locations/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/locations/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "external_id": "585069a1ee19ad271e9b4baf",
        "google_id": 9075393,
        "name": "New York",
        "canonical_name": "New York,New York,United States",
        "country_code": "US",
        "target_type": "City",
        "reach": 8400000,
        "latitude": 40.7127753,
        "longitude": -74.0059728,
        "parent": {
            "id": 2,
            "name": "New York",
            "canonical_name": "New York,United States"
        },
        "children": [
            {
                "id": 3,
                "name": "Manhattan",
                "canonical_name": "Manhattan,New York,United States"
            }
        ]
    },
    "status": "success",
    "message": "Location retrieved successfully.",
    "code": 200
}
 

Request      

GET api/v1/locations/{location_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

location_id   integer   

The ID of the location. Example: 1

location   integer   

The location ID. Example: 1

List Search Results

requires authentication

Get paginated search results with optional filtering by keyword or location. Historical data requires Enterprise plan.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/searches?keyword_id=1&location_id=1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/searches"
);

const params = {
    "keyword_id": "1",
    "location_id": "1",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/searches';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'keyword_id' => '1',
            'location_id' => '1',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/searches'
params = {
  'keyword_id': '1',
  'location_id': '1',
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, params=params)
response.json()

Example response (200):


{
    "data": [
        {
            "id": 1,
            "keyword_id": 1,
            "location_id": 1,
            "total_results": 1250000,
            "searched_at": "2024-01-20T10:00:00Z",
            "keyword": {
                "id": 1,
                "keyword": "rank tracker"
            },
            "location": {
                "id": 1,
                "name": "New York",
                "country_code": "US"
            }
        }
    ]
}
 

Request      

GET api/v1/searches

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

keyword_id   integer  optional  

Filter by keyword ID. Example: 1

location_id   integer  optional  

Filter by location ID. Example: 1

Store Search Results

requires authentication

Store new search results for a keyword and location combination.

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/searches" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"keyword_id\": 1,
    \"location_id\": 1,
    \"results\": [
        \"architecto\"
    ],
    \"total_results\": 1250000
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/searches"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "keyword_id": 1,
    "location_id": 1,
    "results": [
        "architecto"
    ],
    "total_results": 1250000
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/searches';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'keyword_id' => 1,
            'location_id' => 1,
            'results' => [
                'architecto',
            ],
            'total_results' => 1250000,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/searches'
payload = {
    "keyword_id": 1,
    "location_id": 1,
    "results": [
        "architecto"
    ],
    "total_results": 1250000
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):


{
    "id": 1,
    "keyword_id": 1,
    "location_id": 1,
    "results": [],
    "total_results": 1250000,
    "searched_at": "2024-01-20T10:00:00Z",
    "keyword": {
        "id": 1,
        "keyword": "rank tracker"
    },
    "location": {
        "id": 1,
        "name": "New York"
    }
}
 

Request      

POST api/v1/searches

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

keyword_id   integer   

The keyword ID. Example: 1

location_id   integer   

The location ID. Example: 1

results   string[]  optional  

Search results data.

total_results   integer  optional  

Total number of results found. Example: 1250000

Perform Search

requires authentication

Execute a search for a keyword/location combination. Returns cached results if available, otherwise performs new search.

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/searches/perform" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"keyword_id\": 1,
    \"location_id\": 1,
    \"force_new\": false
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/searches/perform"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "keyword_id": 1,
    "location_id": 1,
    "force_new": false
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/searches/perform';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'keyword_id' => 1,
            'location_id' => 1,
            'force_new' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/searches/perform'
payload = {
    "keyword_id": 1,
    "location_id": 1,
    "force_new": false
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "keyword_id": 1,
        "location_id": 1,
        "results": {},
        "total_results": 1250000,
        "searched_at": "2024-01-20T10:00:00Z",
        "from_cache": false
    },
    "status": "success",
    "message": "Search completed successfully.",
    "code": 200
}
 

Example response (429):


{
    "data": null,
    "status": "error",
    "message": "Search limit exceeded. Please try again later.",
    "code": 429
}
 

Request      

POST api/v1/searches/perform

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

keyword_id   integer   

The keyword ID. Example: 1

location_id   integer   

The location ID. Example: 1

force_new   boolean  optional  

Force a new search even if cached results exist. Example: false

Get Search Details

requires authentication

Get detailed information about a specific search including full results.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/searches/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/searches/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/searches/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/searches/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "id": 1,
    "keyword_id": 1,
    "location_id": 1,
    "results": {
        "url1": "example.com",
        "url2": "test.com"
    },
    "total_results": 1250000,
    "searched_at": "2024-01-20T10:00:00Z",
    "keyword": {
        "id": 1,
        "keyword": "rank tracker"
    },
    "location": {
        "id": 1,
        "name": "New York",
        "canonical_name": "New York,New York,United States"
    }
}
 

Request      

GET api/v1/searches/{search_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

search_id   integer   

The ID of the search. Example: 1

search   integer   

The search ID. Example: 1

Team Projects

Manage SERP tracking projects for your team. Create, update, and organize your website tracking! 🏗️

List team projects

requires authentication

Get all projects for a specific team with keyword counts.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/teams/1/projects" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/projects"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/projects';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/projects'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": [
        {
            "id": 1,
            "name": "My Website",
            "domain": "https://example.com",
            "location": "United States",
            "language": "en",
            "device": "desktop",
            "keywords_count": 25,
            "created_at": "2024-01-15T10:30:00Z"
        }
    ],
    "status": "success",
    "message": "Projects retrieved successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to access this team.",
    "code": 403
}
 

Request      

GET api/v1/teams/{team_id}/projects

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Create new project

requires authentication

Create a new SERP tracking project for your team.

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/teams/1/projects" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"\\\"My Awesome Website\\\"\",
    \"domain\": \"\\\"https:\\/\\/example.com\\\"\",
    \"location\": \"\\\"United States\\\"\",
    \"language\": \"\\\"en\\\"\",
    \"device\": \"\\\"desktop\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/projects"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "\"My Awesome Website\"",
    "domain": "\"https:\/\/example.com\"",
    "location": "\"United States\"",
    "language": "\"en\"",
    "device": "\"desktop\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/projects';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => '"My Awesome Website"',
            'domain' => '"https://example.com"',
            'location' => '"United States"',
            'language' => '"en"',
            'device' => '"desktop"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/projects'
payload = {
    "name": "\"My Awesome Website\"",
    "domain": "\"https:\/\/example.com\"",
    "location": "\"United States\"",
    "language": "\"en\"",
    "device": "\"desktop\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):


{
    "data": {
        "id": 1,
        "name": "My Awesome Website",
        "domain": "https://example.com",
        "location": "United States",
        "language": "en",
        "device": "desktop",
        "active": true,
        "created_at": "2024-01-20T10:00:00Z"
    },
    "status": "success",
    "message": "Project created successfully.",
    "code": 201
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to create projects for this team.",
    "code": 403
}
 

Request      

POST api/v1/teams/{team_id}/projects

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Body Parameters

name   string   

Project name. Example: "My Awesome Website"

domain   string   

Website domain to track. Example: "https://example.com"

location   string  optional  

Target location for rankings. Example: "United States"

language   string  optional  

Language code. Example: "en"

device   string  optional  

Device type for rankings. Example: "desktop"

Get project details

requires authentication

Retrieve detailed information about a project including all keywords and latest rankings.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/teams/1/projects/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/projects/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/projects/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/projects/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My Awesome Website",
        "domain": "https://example.com",
        "location": "United States",
        "keywords": [
            {
                "id": 1,
                "keyword": "best seo tools",
                "latest_ranking": {
                    "position": 3,
                    "checked_at": "2024-01-20T08:00:00Z"
                }
            }
        ]
    },
    "status": "success",
    "message": "Project retrieved successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to access this project.",
    "code": 403
}
 

Request      

GET api/v1/teams/{team_id}/projects/{project_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

project_id   integer   

The ID of the project. Example: 1

team   integer   

The team ID. Example: 1

project   integer   

The project ID. Example: 1

Update project

requires authentication

Update project settings and configuration.

Example request:
curl --request PATCH \
    "http://rank-tracker.test/api/v1/teams/1/projects/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"\\\"Updated Website Name\\\"\",
    \"domain\": \"\\\"https:\\/\\/newdomain.com\\\"\",
    \"location\": \"\\\"Canada\\\"\",
    \"language\": \"\\\"fr\\\"\",
    \"device\": \"\\\"mobile\\\"\",
    \"active\": false
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/projects/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "\"Updated Website Name\"",
    "domain": "\"https:\/\/newdomain.com\"",
    "location": "\"Canada\"",
    "language": "\"fr\"",
    "device": "\"mobile\"",
    "active": false
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/projects/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => '"Updated Website Name"',
            'domain' => '"https://newdomain.com"',
            'location' => '"Canada"',
            'language' => '"fr"',
            'device' => '"mobile"',
            'active' => false,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/projects/1'
payload = {
    "name": "\"Updated Website Name\"",
    "domain": "\"https:\/\/newdomain.com\"",
    "location": "\"Canada\"",
    "language": "\"fr\"",
    "device": "\"mobile\"",
    "active": false
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PATCH', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "Updated Website Name",
        "domain": "https://newdomain.com",
        "active": false,
        "updated_at": "2024-01-20T10:30:00Z"
    },
    "status": "success",
    "message": "Project updated successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to update this project.",
    "code": 403
}
 

Request      

PATCH api/v1/teams/{team_id}/projects/{project_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

project_id   integer   

The ID of the project. Example: 1

team   integer   

The team ID. Example: 1

project   integer   

The project ID. Example: 1

Body Parameters

name   string  optional  

Project name. Example: "Updated Website Name"

domain   string  optional  

Website domain. Example: "https://newdomain.com"

location   string  optional  

Target location. Example: "Canada"

language   string  optional  

Language code. Example: "fr"

device   string  optional  

Device type. Example: "mobile"

active   boolean  optional  

Enable/disable project. Example: false

Delete project

requires authentication

Delete a project and all associated keywords and ranking data.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/teams/1/projects/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/projects/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/projects/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/projects/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": null,
    "status": "success",
    "message": "Project deleted successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to delete this project.",
    "code": 403
}
 

Request      

DELETE api/v1/teams/{team_id}/projects/{project_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

project_id   integer   

The ID of the project. Example: 1

team   integer   

The team ID. Example: 1

project   integer   

The project ID. Example: 1

Teams

Manage your SERP crew! Create teams, invite members, and collaborate on projects. 🤙

List teams

requires authentication

Get all teams you're a member of. Admins can see all teams.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/teams?page=1&per_page=20" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"per_page\": 1
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams"
);

const params = {
    "page": "1",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "per_page": 1
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'page' => '1',
            'per_page' => '20',
        ],
        'json' => [
            'per_page' => 1,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams'
payload = {
    "per_page": 1
}
params = {
  'page': '1',
  'per_page': '20',
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers, json=payload, params=params)
response.json()

Example response (200):


{
    "data": {
        "teams": [
            {
                "id": 1,
                "name": "My SERP Crew",
                "slug": "my-serp-crew",
                "owner": {
                    "id": 1,
                    "name": "John Doe",
                    "email": "john@example.com"
                },
                "members_count": 3,
                "projects_count": 5
            }
        ],
        "meta": {
            "current_page": 1,
            "total": 25,
            "per_page": 20,
            "last_page": 2
        }
    },
    "status": "success",
    "message": "Teams retrieved successfully.",
    "code": 200
}
 

Example response (422):


{
    "data": null,
    "status": "error",
    "message": "The limit is up to 100 teams per page. Please try again with a lower per page request.",
    "code": 422
}
 

Request      

GET api/v1/teams

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

page   integer  optional  

Page number for pagination. Example: 1

per_page   integer  optional  

Results per page (max 100). Example: 20

Body Parameters

per_page   integer  optional  

Must be at least 1. Must not be greater than 100. Example: 1

Create team

requires authentication

Create a new team and become the owner. Start building your SERP crew!

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/teams" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"\\\"My SERP Crew\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "\"My SERP Crew\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => '"My SERP Crew"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams'
payload = {
    "name": "\"My SERP Crew\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (201):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "slug": "my-serp-crew",
        "owner_id": 1,
        "owner": {
            "id": 1,
            "name": "John Doe",
            "email": "john@example.com"
        },
        "members": [
            {
                "id": 1,
                "name": "John Doe",
                "role": "owner"
            }
        ],
        "created_at": "2024-01-20T10:00:00Z"
    },
    "status": "success",
    "message": "Team created successfully.",
    "code": 201
}
 

Request      

POST api/v1/teams

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

name   string   

Team name. Example: "My SERP Crew"

Get team details

requires authentication

Retrieve detailed information about a team including members and projects.

Example request:
curl --request GET \
    --get "http://rank-tracker.test/api/v1/teams/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('GET', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "slug": "my-serp-crew",
        "owner": {
            "id": 1,
            "name": "John Doe"
        },
        "members": [
            {
                "id": 1,
                "name": "John Doe",
                "role": "owner"
            },
            {
                "id": 2,
                "name": "Jane Smith",
                "role": "member"
            }
        ],
        "projects": [
            {
                "id": 1,
                "name": "Website Project",
                "domain": "https://example.com",
                "keywords_count": 25
            }
        ]
    },
    "status": "success",
    "message": "Team retrieved successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to access this team.",
    "code": 403
}
 

Request      

GET api/v1/teams/{team_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Update team name

requires authentication

Update the team's name. Only team owners can update team names.

Example request:
curl --request PATCH \
    "http://rank-tracker.test/api/v1/teams/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"\\\"Updated SERP Crew\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "\"Updated SERP Crew\""
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => '"Updated SERP Crew"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1'
payload = {
    "name": "\"Updated SERP Crew\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PATCH', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "Updated SERP Crew",
        "slug": "my-serp-crew",
        "updated_at": "2024-01-20T10:30:00Z"
    },
    "status": "success",
    "message": "Team name updated successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to update this team.",
    "code": 403
}
 

Request      

PATCH api/v1/teams/{team_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Body Parameters

name   string  optional  

Team name. Example: "Updated SERP Crew"

Delete team

requires authentication

Delete a team and all associated projects and data. Only team owners can delete teams.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/teams/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": null,
    "status": "success",
    "message": "Team deleted successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to delete this team.",
    "code": 403
}
 

Request      

DELETE api/v1/teams/{team_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Add team member

requires authentication

Invite a user to join your SERP crew! User must already have an account.

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/teams/1/members" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"\\\"newmember@example.com\\\"\",
    \"role\": \"\\\"member\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/members"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "\"newmember@example.com\"",
    "role": "\"member\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/members';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => '"newmember@example.com"',
            'role' => '"member"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/members'
payload = {
    "email": "\"newmember@example.com\"",
    "role": "\"member\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "members": [
            {
                "id": 1,
                "name": "John Doe",
                "role": "owner"
            },
            {
                "id": 2,
                "name": "New Member",
                "role": "member"
            }
        ]
    },
    "status": "success",
    "message": "Team member added successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to add members to this team.",
    "code": 403
}
 

Request      

POST api/v1/teams/{team_id}/members

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Body Parameters

email   string   

User's email address. Example: "newmember@example.com"

role   string   

Member role (member, viewer). Example: "member"

Update team member role

requires authentication

Change a team member's role. Only team owners can update member roles.

Example request:
curl --request PATCH \
    "http://rank-tracker.test/api/v1/teams/1/members/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"role\": \"\\\"viewer\\\"\"
}"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/members/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "role": "\"viewer\""
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/members/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'role' => '"viewer"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/members/1'
payload = {
    "role": "\"viewer\""
}
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('PATCH', url, headers=headers, json=payload)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "members": [
            {
                "id": 1,
                "name": "John Doe",
                "role": "owner"
            },
            {
                "id": 2,
                "name": "Jane Smith",
                "role": "viewer"
            }
        ]
    },
    "status": "success",
    "message": "Team member role updated successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to update member roles for this team.",
    "code": 403
}
 

Request      

PATCH api/v1/teams/{team_id}/members/{user_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

user_id   integer   

The ID of the user. Example: 1

team   integer   

The team ID. Example: 1

user   integer   

The user ID to update. Example: 2

Body Parameters

role   string   

New member role (member, viewer). Example: "viewer"

Remove team member

requires authentication

Remove a member from your team. Only team owners can remove members.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/teams/1/members/1" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/members/1"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/members/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/members/1'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": null,
    "status": "success",
    "message": "Team member removed successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to remove members from this team.",
    "code": 403
}
 

Request      

DELETE api/v1/teams/{team_id}/members/{user_id}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

user_id   integer   

The ID of the user. Example: 1

team   integer   

The team ID. Example: 1

user   integer   

The user ID to remove. Example: 2

Change team plan

requires authentication

Upgrade or downgrade team to a different plan. Handles billing for paid plans.

Example request:
curl --request POST \
    "http://rank-tracker.test/api/v1/teams/1/change-plan/&quot;professional&quot;" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/change-plan/&quot;professional&quot;"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/change-plan/"professional"';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/change-plan/&quot;professional&quot;'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('POST', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "plan": {
            "name": "Pro SERPer",
            "slug": "pro",
            "price": 29.99
        },
        "subscription_status": "active"
    },
    "status": "success",
    "message": "Team plan updated successfully.",
    "code": 200
}
 

Example response (403):


{
    "data": null,
    "status": "error",
    "message": "You don't have permission to change this team's plan.",
    "code": 403
}
 

Example response (422):


{
    "data": null,
    "status": "error",
    "message": "Invalid plan selected.",
    "code": 422
}
 

Request      

POST api/v1/teams/{team_id}/change-plan/{plan}

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

plan   string   

Plan slug (starter, professional, agency). Example: "professional"

team   integer   

The team ID. Example: 1

Cancel team subscription

requires authentication

Cancel the team's paid subscription at the end of the current billing period.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/teams/1/subscription" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/subscription"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/subscription';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/subscription'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "plan": {
            "name": "Pro SERPer",
            "slug": "professional",
            "price": 4900
        },
        "subscription_status": "cancelled",
        "ends_at": "2025-08-30T23:59:59Z"
    },
    "status": "success",
    "message": "Subscription cancelled successfully. You'll retain access until August 30, 2025.",
    "code": 200
}
 

Request      

DELETE api/v1/teams/{team_id}/subscription

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1

Cancel team subscription immediately

requires authentication

Immediately cancel and forfeit remaining billing period access.

Example request:
curl --request DELETE \
    "http://rank-tracker.test/api/v1/teams/1/subscription/immediate" \
    --header "Authorization: Bearer {your_api_token}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "http://rank-tracker.test/api/v1/teams/1/subscription/immediate"
);

const headers = {
    "Authorization": "Bearer {your_api_token}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'http://rank-tracker.test/api/v1/teams/1/subscription/immediate';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {your_api_token}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json

url = 'http://rank-tracker.test/api/v1/teams/1/subscription/immediate'
headers = {
  'Authorization': 'Bearer {your_api_token}',
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
response.json()

Example response (200):


{
    "data": {
        "id": 1,
        "name": "My SERP Crew",
        "plan": null,
        "subscription_status": "cancelled_immediately",
        "cancelled_at": "2025-07-30T12:00:00Z"
    },
    "status": "success",
    "message": "Subscription cancelled immediately. Historical data preserved up to July 30, 2025.",
    "code": 200
}
 

Request      

DELETE api/v1/teams/{team_id}/subscription/immediate

Headers

Authorization      

Example: Bearer {your_api_token}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

team_id   integer   

The ID of the team. Example: 1

team   integer   

The team ID. Example: 1