DocumentationUse your data

API

Read and write your projects, keywords and positions from your own tools.

The API is included in every account, at no extra cost. Anything the interface does with your projects, keywords and positions can be done over HTTP.

One API callGET /api/v1/keywords?project=42Authorization: Bearer pt_live_…{ "keyword": "acme shop", "position": 1, "best": 1, "url": "acme-shop.com" }Your toolsInternal dashboardsClient reportsSlack or email alertsThe API is included in every account, at no extra cost.
An authenticated request, a JSON response

Get a key

  1. 1Open Settings, then the Account tabThe API key section is at the bottom of the tab.
  2. 2Click GenerateThe key starts with pt_live_. Copy it straight away: it is shown once, because we keep only a hash of it and its first characters. Treat it like a password.
  3. 3Revoke or regenerate whenever you wantRegenerate replaces the existing key, Revoke deletes it. Either way the old key stops working immediately.
A key grants access to all of your data. Keep it server-side, never in a site or an app you ship to the public.

Authentication

Every request carries the key in the X-API-Key header. The base URL is https://tool.positiontiger.com/api.

curl https://tool.positiontiger.com/api/projects \
  -H "X-API-Key: pt_live_your_key"

Without a valid key the response is a 401 with {"error":"Unauthorized"}.

The resources

ResourceWhat it does
/projectsList, create, rename and delete your projects, add your sites to them or remove them.
/sitesList the projects you can reach, including ones shared with you, create and delete them: here a "site" is a project.
/keywordsList, add and delete keywords (by term, or one tracking row at a time), force a refresh, add or remove tags.
/positionsRead the positions that were checked: this is the endpoint that feeds a dashboard.
/competitorsList, add and remove competitors on a project.
/tagsCreate, rename, colour and delete a project's tags.

Reading positions

GET /api/positions is the richest resource. Only the project is required: its name (project) or its id (projectId, from GET /api/projects). Wherever the API takes project, you can give projectId instead.

A project name is unique within your account. If older projects share a name, project always means the oldest of them when reading, and the response says so in meta (sameName lists the other ids, note explains). A write (adding, deleting, refreshing, tags, competitors) with such a name is refused (409, with the ids): use projectId then.

curl "https://tool.positiontiger.com/api/positions?project=Acme%20Shop&days=30&device=desktop&include=all" \
  -H "X-API-Key: pt_live_your_key"
ParameterValuesDefault
projectthe project's namerequired unless projectId
projectIdthe project's id, instead of its name—
days1 to 36590
startDate, endDateYYYY-MM-DD. endDate alone: the days before itthe days window
devicedesktop, mobile, allall
keywordscomma-separated listall
tagscomma-separated list. A tag the project does not have matches no keyword (an empty list)all
siteone of the project's sites (its exact domain, subdomains included): positions are measured for that site, over every keyword. Text matching several sites returns a 400 listing themeach keyword's tracked site, failing that the project's first own site
minPosition, maxPosition1 to 60no filter
hideUnrankedtrue to drop unranked keywordsfalse
sortByposition, volume, cpc, term, date, evolutionposition
orderasc or descasc for position
limit, offsetlimit up to 500100 and 0
includevolume, cpc, history, allno extras

Each row returns the keyword, the position, the previous position, the change, the ranking URL, the date, the device, the location, the tracked site and the tags. meta.filtered gives the total, which is what you page through with offset.

Your site counts with all its subdomains (fr.example.com, blog.example.com), as in the dashboard: the position is the best of them, and url says which.

The position and date are those of the latest check, as in the dashboard and reports: a keyword checked while your site was not in the results returns "position": null, dated that check. lastRankedPosition and lastRankedDate give the last time your site ranked (null if it never has). previousPosition is the position at the previous check. With a past endDate, the position and date are those of the latest check up to that date, included.

{
  "keywords": [
    {
      "term": "acme shop",
      "position": 1,
      "previousPosition": 2,
      "evolution": 1,
      "url": "https://acme-shop.com/",
      "date": "2026-09-22T00:00:00Z",
      "lastRankedPosition": 1,
      "lastRankedDate": "2026-09-22T00:00:00Z",
      "device": "desktop",
      "location": "FR",
      "site": "https://acme-shop.com",
      "tags": ["brand"]
    }
  ],
  "meta": { "project": "Acme Shop", "projectId": 4746, "filtered": 381, "returned": 100, "limit": 100, "offset": 0 }
}

Dates are YYYY-MM-DDT00:00:00Z (the day of the check, UTC). If positions cannot be read at that moment, the response is a 503 with a Retry-After header: try again a few minutes later (no position is ever returned empty by mistake).

Adding a keyword

curl -X POST https://tool.positiontiger.com/api/keywords \
  -H "X-API-Key: pt_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"project":"Acme Shop","term":"cordless drill","location":"FR","lang":"fr","device":"desktop"}'

A keyword the project already tracks (same term, country, language and device) returns a 409, and nothing is charged. The same term in another country or on the other device is another keyword. A keyword another of your projects already tracks (same term, country, language and device) is free only if you already paid for it today (UTC day): it shares that check. Otherwise it costs 6 pages and is checked right away. If your balance is empty the response is a 403 with the code INSUFFICIENT_CREDITS.

location is the country code (FR when omitted). A country we do not track positions in is refused: 400, with every country and its code listed in countries, and nothing is charged.

lang is the search language, the country's own when omitted. Any language is accepted with any country, as in the dashboard: positions are asked of Google in that country (gl) in that language (hl), which is what someone in that country with Google set to that language sees. When Google Ads has no volume for that language in that country, the volume of the country's languages is shown.

Projects

MethodPathWhat it does
GET/api/projectsLists every project, with its keyword count, tracked sites and competitors. ?include=keywords adds the keywords.
GET/api/projects/{id}Fetches one project by id.
POST/api/projectsCreates a project: name, optionally trackedSites and logo.
PUT/api/projectsRenames a project or adds sites to it: projectId (or name) and newName, logo, trackedSites. trackedSites adds the sites the project does not have yet: the ones not in the list stay, and a site already there (as a site or a competitor) is not added a second time.
DELETE/api/projects/sitesRemoves one or several of your sites from a project: projectId (or project), url or urls. For a competitor, use DELETE /api/competitors.
DELETE/api/projectsDeletes a project and everything in it: projectId (or name). A name two projects share returns a 409 with their ids.

DELETE /api/projects/sites recognises a site by its domain, with or without https://, whatever the path (www.example.com and https://www.example.com/en are the same site). It never removes the project's last own site: the request is then refused (409) and nothing is removed; add another site first. The project's keywords and their history stay. The response gives the number of sites removed (deleted) and the ones left (trackedSites).

Sites

In /api/sites, a "site" is a project: siteId is a project's id. To add or remove one of the sites tracked in a project, see PUT /api/projects and DELETE /api/projects/sites above.

MethodPathWhat it does
GET/api/sitesLists the projects you can reach, including ones shared with you, with your role. The other resources read only your own projects: a shared project's id returns a 404 there.
GET/api/sites/{id}Fetches one project by id.
POST/api/sitesCreates a project: name, optionally logo.
DELETE/api/sitesDeletes the whole project, with its keywords, sites and competitors: siteId, the project's id. To remove just one of your sites, use DELETE /api/projects/sites.

Keywords

MethodPathWhat it does
GET/api/keywordsLists your keywords with their location, language, device and tags. ?project= narrows to one project.
GET/api/keywords/{id}Fetches one keyword by its tracking id — the one GET /api/keywords returns, not the keywordId.
POST/api/keywordsAdds a keyword: project, term, optionally location, lang, device.
DELETE/api/keywordsDeletes keywords by term: project and terms. Each term is deleted from the project in every country and on both devices.
DELETE/api/keywords/{id}Deletes a single tracking row: one keyword, in one project, one country, on one device. id is the one GET /api/keywords returns, not the keywordId. A row that is not in one of your projects returns a 404.
POST/api/keywords/refreshQueues keywords for a check straight away: project (or projectId), a keywords list, or "mode": "all" for every keyword of the account.
POST/api/keywords/tagsApplies a tag to a list of keywords: project, keywords, tag.
DELETE/api/keywords/tagsRemoves that tag from the same keywords.

Every keyword carries two separate identifiers, and they are not interchangeable. id is your tracking row: it belongs to your account and it is what GET and DELETE /api/keywords/{id} take. keywordId is the keyword itself — the term, language and country together — shared by every account tracking that term. The two run as separate sequences, so passing one where the other is expected addresses an unrelated keyword. GET /api/keywords returns both, for reference. Reading positions needs no identifier at all: GET /api/positions works from the project name and the terms.

A delete, by term or by id, removes only your tracking rows. A check already running when you delete is made, and counted; a check still waiting leaves the queue, unless the keyword is still tracked on that device.

POST /api/keywords/refresh must always say what to refresh: a project (project or projectId), a non-empty list of terms (keywords, across all your projects when none is given), or "mode": "all" for every keyword of the account, sent alone: the body is exactly {"mode": "all"}, and any other field beside it is refused (400). An empty body, {} or a misspelt field is refused (400): nothing is refreshed or charged. Every check is counted, 6 pages per keyword and device, even if the keyword was already checked today.

Positions

One resource, GET /api/positions, whose parameters do all the work. The combinations that earn their keep:

CallWhat it returns
?project=NomEvery position in the project over the last 90 days.
&maxPosition=10The Top 10 only.
&include=volume,cpcSearch volume and CPC on every row: your own figures where you corrected them on the dashboard, as it shows them.
&include=history&days=30The history over the period: one point per day your site ranked (unranked days are left out).
&device=mobileMobile positions only.
&minPosition=1&maxPosition=50&hideUnranked=trueA slice of positions, with unranked keywords dropped.

Competitors

MethodPathWhat it does
GET/api/competitors?project=Lists a project's competitors.
GET/api/competitors/{id}Fetches one competitor by id.
POST/api/competitorsAdds one or several competitors: project, url or urls. added counts the ones actually added: a site the project already has, as a competitor or as a site, is not added a second time.
DELETE/api/competitorsRemoves one or several competitors from the project: project, url or urls.

Tags

MethodPathWhat it does
GET/api/tags?project=Lists a project's tags, with how many keywords carry each.
GET/api/tags/{id}Fetches one tag by id.
POST/api/tagsCreates a tag: project, name, color.
PUT/api/tagsRenames a tag or changes its colour.
DELETE/api/tagsDeletes a tag: project, name or names.

Try it safely

The dashboard's API Playground lists the endpoints, generates code in a dozen languages and shows a sample response. Requests run from the playground do not change your projects.

PositionTiger
Reports
381keywords
100%
227,008result pages
API
N

PositionTiger API

Projects

GETList all your projectsGETGet a project by idPOSTCreate a projectPUTRename a projectDELETEDelete a project

Sites

GETList all your sitesGETGet a site by idPOSTCreate a siteDELETEDelete a site

Keywords

GETList all your keywordsPOSTAdd a keywordPOSTRefresh keywords

Request

GET/api/projects

Show keyRun
ccURLJJavaScriptPPythonPPHPRRubyGGoNNode.jsPPowerShellCC#JJavaSSwiftRR
curl -X GET "https://tool.positiontiger.com/api/projects" \
  -H "X-API-Key: pt_••••••••••••••••••••a4f2" \
  -H "Content-Type: application/json"

Response

200 OK

Example data. Running a request here does not change your projects.

{
  "projects": [
    {
      "id": 1,
      "name": "Jardin & Terrasse",
      "keywordCount": 203,
      "trackedSites": ["https://jardin-terrasse.fr"],
      "competitors": ["https://gamm-vert.fr", "https://truffaut.com"]
    },
    {
      "id": 2,
      "name": "Mobilier intérieur",
      "keywordCount": 84,
      "trackedSites": ["https://mobilier-inte.fr"],
      "competitors": ["https://conforama.fr"]
    }
  ],
  "total": 2
}
The playground: the endpoints, the request, the response

A question this does not answer? Write to us.