Skip to content

Open Patrol data API

You were given a token. This page says what it reads and how.

What it is

The API serves the facts Open Patrol publishes on its website, as data: which game stood in which place of which row on which casino's homepage on which day, plus the lists those facts refer to (markets, editions, casinos, studios, games, licences).

It serves facts, not figures. There are no shares, ranks or top lists. If you need a share, compute it from the placements. A figure you compute may differ from the one on the website, because the website applies its own counting rules.

It is read-only. Every request is a GET.

The token

Send it in one header on every request:

Authorization: Bearer <your token>

Base address

https://open.playpatrol.app/api/data/v1

GET /api/data/v1 itself lists the addresses below. It is the quickest way to check that your token works.

Answers

Every JSON answer has the same shape:

{"data": [ ... ], "next": null}

data is a list of rows. next is the address of the next page, or null when there is none. To read everything, request next until it is null. Use the address as given; do not build it yourself.

Dates are YYYY-MM-DD. Addresses in the rows are absolute.

The seven addresses

/markets

The markets that have at least one published edition. No parameters.

Key Meaning
code the market's code, upper case: ESP, or a US state's ISO 3166-2 code. This is how every other address names a market
iso2 the two-letter country code
name the market's name
language the market's language
timezone the market's time zone
regulator the regulator whose licence register we read: name, register_url, read_on (the day we read it). null if we have not read one

/markets/{geo}/licences

The licence list of one market, the same rows the market's page on the website shows. {geo} is the market's code. No parameters. The list can be empty for a market whose register we have not read yet.

Key Meaning
name the casino's name
domain the casino's domain
holder the licence holder, or null
number the licence number, or null
monitored true if we scan this casino's homepage in this market

/editions

The published editions. An edition is one scan of one market. Parameter: market (optional).

Key Meaning
market the market's code
number the edition's number within its market
scanned_on the day of the scan
url the report's page on the website

/casinos

The casinos that appear in a published edition. Parameter: market (optional). It chooses which casinos are listed; a casino's row is the same either way.

Key Meaning
domain the casino's domain. This is how placements name a casino
name the casino's name as the website shows it
markets the codes of the markets the casino was read in, among those your token covers
licence number, issuer (may be empty), checked_on (may be null), register_url (may be null). The whole key is null if we hold no licence number, or if the casino has no page on the website
url the casino's page on the website, or null if it has none

/providers

The game studios in our catalogue that appear in a published edition, as the studio a casino showed or as the maker of a game it showed, so the list may hold a studio whose games casinos show only under other names. No parameters.

Key Meaning
id the studio's id. This is how games and placements name a studio
name the studio's name
url the studio's page on the website, or null if it has none
group the studio group the studio belongs to, or is: id (the group's own studio id), name (the group's bare name, "Evolution", as the group's page prints it) and url (the group's page on the website, or null). null for a studio in no group

/games

The games in our catalogue that appear in a published edition. Parameters: provider (a studio's id, optional), limit (default 1000, at most 5000), after (taken from next).

Key Meaning
id the game's id. This is how placements name a game
title the game's title as the website shows it
provider_id the id of the game's studio
url the game's page on the website, or null if it has none

/placements

The placements themselves. One row is one game in one place of one row of one casino's homepage, in one edition.

Key Meaning
market the market's code
edition the edition's number
scanned_on the day this placement was recorded
casino the casino's domain
row the position of the row on the homepage
row_name the row's title as the casino wrote it
position the game's position in that row, as recorded. Positions may have gaps
game_id the game's id in our catalogue, or null if the game is not in it
game the game's title as the website spells it; when game_id is null, the recorded text in that spelling
provider_id the studio's id in our catalogue, or null if the studio is not in it
provider the studio's name. When provider_id is null, the text the scan recorded, or null when the scan could not identify the studio: a name without an id is a studio our catalogue does not hold, and both null means not identified
capture the address of our screenshot of that casino's homepage in that edition, or null
page_tiles the count of tiles recorded on that casino's page in that edition, as /evidence/{casino}/{day} prints it; no share is served
maker_id the id of the studio our catalogue records as the game's maker, or null when the catalogue does not name one
maker that studio's name, or null with maker_id. It is the studio a game's page names as its maker, and it differs from provider where the casino showed a publisher's name

Nothing is left out: a placement whose studio we could not identify is served too, with a null id.

A placement's provider is the studio the casino showed for that game, which may be a publisher rather than the studio that made it; the provider filter reads the same studio. maker_id and maker name the studio that made it, as a game's page does; no filter reads them. A game's provider_id in the games list is the studio our catalogue records as its maker.

Filters. All optional, and they combine.

Parameter Value
market a market's code
edition an edition's number. Only together with market
casino a casino's domain
provider a studio's id
game a game's id
from, to scan dates, inclusive, YYYY-MM-DD

A casino, studio or game we do not know gives an empty list, not an error.

Order. Always the same: by market, then edition number, then the order the placements were recorded.

Paging. limit is 1000 by default and at most 5000. Follow next until it is null. For a full walk of a large market, use limit=5000, or use CSV. A page can occasionally hold a few rows more than limit. A walk never skips a row and never repeats one. A limit above the maximum is a 400.

Placements as CSV

Add format=csv to /placements. You get the same rows as a table: one header row with the fifteen keys above, in that order, then one line per placement. Empty cells stand for null.

Opening the CSV in a spreadsheet

The file is UTF-8 without a byte-order mark.

  1. Google Sheets: File > Import > Upload. It reads the file correctly.
  2. LibreOffice: open the file and choose the character set UTF-8 in the import dialog.
  3. Excel: do not open the file by double-click, or accented letters will be garbled. Use Data > From Text/CSV and choose the file origin "65001: Unicode (UTF-8)".
  4. If a name shows a leading apostrophe, that is the protection described above.

Use it from an AI assistant (MCP)

The same facts can be read by an AI assistant that speaks the Model Context Protocol (MCP). The assistant calls tools; seven of them are the addresses above, and answer with the same rows. Two more serve Patrol Rank, Open Patrol's own ranking of studios and games, as the ranking pages print it.

https://open.playpatrol.app/mcp

Connecting with a token

Send the token in the Authorization header, exactly as for the addresses above. In Claude Code:

claude mcp add --transport http open-patrol https://open.playpatrol.app/mcp \
  --header "Authorization: Bearer <your token>"

Any other client that can send a header to a remote MCP server works the same way. As above, the token is accepted in the header only, never in the address.

Connecting by signing in

An assistant that can sign in does not need a token. It sends you to Open Patrol, you sign in with the address your monitoring is under and allow the access, and the assistant is connected. The steps for each assistant are at open.playpatrol.app/connect.

The page you allow shows the assistant's name, as it names itself, and where it will send you back to. Allow it only if you started the connection just now. The assistant reads what your plan covers on the day it asks, and nothing it can do changes anything. You can disconnect each assistant on its own from your account page, under Data access, MCP, and doing so does not touch your token. An assistant that has not been used for 30 days has to be signed in again.

Every request to /mcp is a POST with a JSON body. Any other method is a 405.

The fourteen tools

Tool Mirrors Arguments
list_markets /markets none
list_licences /markets/{geo}/licences market (required)
list_editions /editions market
list_casinos /casinos market
list_providers /providers none
list_games /games provider, limit, after
list_placements /placements market, edition, casino, provider, game, from, to, max_page_position, limit, after
patrol_rank the Patrol Rank pages kind (studios, games or groups, required), market, month, limit, after
rank_of the Patrol Rank pages provider or game (one of them), month
publisher_partners a publisher's page publisher or maker (one of them), market, limit, after
group_profile a studio group's page group (required), month
market_month a market's month report market (required), month, limit, after
casino_studios a casino's page casino (required), month
list_news the newsroom month, market, studio, limit, after

The arguments mean what the parameters of the same name mean above. An argument a tool does not know is refused, never ignored. There is no CSV here.

An answer is one block of JSON text with the column names once and then one list of values per row:

{"columns": ["market", "edition", "scanned_on", "..."], "rows": [["ESP", 3, "2026-10-02", "..."]], "next_after": null}

The columns are the keys of the address the tool mirrors, in the same order. Nested keys (regulator, licence) stay objects inside their cell. list_providers adds aggregator, the address of a publisher's page where it has one.

list_placements adds one column at the end, page_position: the game's place on the whole casino homepage in that edition, every row and every studio counted, so 1 is the first game on the page. max_page_position keeps only the placements at or above that place. Neither exists at the /placements address.

patrol_rank answers one ranking: the studios or the games of one market, or of all markets when market is left out. Next to the rows it says which ranking it is (ranking, for example UK Patrol Rank), how a place is decided (rule), the scans it counts (period), the public page that prints it (page) and the months that have one (months; pass one as month). Without month it is the month the public page shows now. rank_of answers one studio's or one game's place in every market's ranking where it is ranked, and across all markets, one row each, labelled the same way. Quote these as Open Patrol's Patrol Rank: they are our ranking, not a recorded fact. patrol_rank with kind groups answers one market's studio groups and independent studios as that market's report prints them, each group with its brands; market is required there. rank_of names a studio's group and the group's place in each market (group, group_rank).

The last five tools each answer what one public page prints. publisher_partners names the studios a publisher brings to casinos, with their placements and markets, or, given a maker, the publishers that bring that studio. group_profile is a studio group's page under the group's bare name: one month (the month the page shows, the newest month every market has read, which may still be under way, or the month you pass; the answer names it), the group's Group Patrol Rank and share that month, its studios with their own Patrol Rank, the markets they were on show in and the groups around it. market_month is a market's month report: casinos read, placements per scan, games and studios seen, the new games by the day each was first seen and the repeats. casino_studios is a casino's page: the studios on its homepage, each with its placements and share, and every studio seen there. list_news lists what the newsroom published, newest first, with the markets and studios each item is about.

What you can read

Every tool returns only what your plan covers, as the API does, and list_markets and list_licences too: the market list is the markets your plan touches, and the licence list of a market your plan does not cover is refused. Your plan is read at each request: something you buy today is covered on the next call. When nothing you bought is current any more, the token is answered 401, here as at the API, until you buy again; the same token then works again. A token issued to you directly reads the markets it was issued for.

A market's Patrol Rank is served for a market your plan covers; the ranking across all markets is served to every plan, as its page is public. The same holds for a group's or a publisher's figures: the totals are every plan's, and every list by market names only the markets your plan covers. A month report and a casino's page are served for a market your plan covers. A news item about a market your plan does not cover is left out; an item about no market is listed for every plan.

Without a market argument, a data tool returns the rows your plan covers and says what that is in a covers key, a list such as ["market GBR"]. When you name a market, studio, casino or game your plan does not cover, the answer is not an error: it is one plain sentence - the API's own message, for example Your plan does not cover that market. - and no rows. A plan that covers nothing answers Your plan does not cover any data.

Paging

list_games, list_placements, patrol_rank, market_month, publisher_partners and list_news return a next_after value, or null on the last page. To continue, pass it back as after, unchanged. limit has a default and a maximum:

Tool Default At most
list_placements 100 120
list_games 200 200
patrol_rank 100 200
market_month 100 120
publisher_partners 25 25
list_news 20 25

These pages are smaller than the data API's, because an assistant reads every row into its working memory. Filter as narrowly as you can. The other tools return the whole list in one answer.

Refusals

A request the tool cannot read, such as an argument it does not know or a malformed value, is an answer marked as an error. It carries the same sentence and the same status the matching address gives, in the body. A request without a usable token is a 401 with a WWW-Authenticate header, and a request from a web page is a 403.

The minute

The limit of 120 requests a minute (see Limits) is counted per customer, across the token on his account page and every assistant he uses, here and at the addresses above together. Every message the assistant sends counts, including the ones it sends to set up the connection.

Limits

120 requests a minute: per account for the token on your account page and every assistant you connect, counted across both doors together (see The minute), and per token for a token issued to you directly. Beyond that you get a 429 with a Retry-After header: wait that many seconds before the next request.

Status codes

Status Meaning
200 the rows
400 a parameter we do not know, or a value we cannot read. The body names it in parameter
401 the token is missing, wrong, revoked or expired, or nothing you bought is current. The answer is the same in every case
403 you named a market, studio, casino or game your plan does not cover (scope_not_purchased)
404 no such address
405 any method but GET; told only to a valid token
429 too many requests; see Limits
500 something went wrong on our side
503 temporarily unavailable. Wait for the time in Retry-After and try again

Every error is JSON: {"error": "..."}. The one exception is the 403, which says what is not covered in a second key:

{"error": "scope_not_purchased", "message": "Your plan does not cover that market."}

The message names one of four things your filters asked for - market, studio (the provider filter), casino or game - or, when each is covered on its own but not together, combination of filters; an edition is named as its market. A casino is judged by the markets it is listed in or was recorded in, and a game by its studio, never by whether any rows match.

An unknown parameter is refused, never ignored. A misspelt filter gives a 400, not a full unfiltered answer.

If your plan does not cover everything, every list shows only what it covers, without an error: a market plan its markets, a studio plan that studio's rows in every market, a Daily Patrol pair only that studio's rows at that casino. Several of these together read each one whole, never mixed: a pair and a market are that pair's rows plus everything in that market, not the pair's studio across the market. Only naming something outside your plan explicitly gives a 403: a market, a studio, a casino or a game. A malformed parameter is still a 400 first.

Examples

Set the token once:

export TOKEN='opd_...'

The markets:

curl -H "Authorization: Bearer $TOKEN" https://open.playpatrol.app/api/data/v1/markets

The first page of Spain's placements, at the largest JSON page size:

curl -H "Authorization: Bearer $TOKEN" "https://open.playpatrol.app/api/data/v1/placements?market=ESP&limit=5000"

Spain's placements as a CSV file, with the response headers saved so you can read Link:

curl -H "Authorization: Bearer $TOKEN" -D headers.txt -o placements.csv "https://open.playpatrol.app/api/data/v1/placements?market=ESP&format=csv&limit=50000"

What is deliberately absent

Changes

v1 is part of the address. We may add keys and addresses to v1, so ignore keys you do not know. We will not remove a key or change what one means within v1.