Every API here is available over the APIs.io API and to AI agents over MCP.
{
"openapi": "3.1.1",
"info": {
"title": "Fairly API",
"description": "Real prices for PC games, including checkout fees, and the cheapest way to play. Data is licensed CC BY 4.0, attribution: \"Fairly\"; game cover images are not covered by that license. Versioning and deprecation policy: https://fairly.gg/developers#versioning. Some store links are affiliate links; Fairly earns a commission on purchases and the price you pay does not change. Ranking is by price, never by commission. Append `?raw=1` to a `/go/{offer_id}` link for the plain store link.",
"version": "v1"
},
"servers": [
{
"url": "https://api.fairly.gg/"
}
],
"paths": {
"/v1/games": {
"get": {
"tags": [
"Games"
],
"summary": "Search games by title",
"description": "Title search (pg_trgm), first 20 results.",
"parameters": [
{
"name": "q",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfGameSummary"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/games/index": {
"get": {
"tags": [
"Games"
],
"summary": "Indexable games for the sitemap",
"description": "Games with at least one active offer or active subscription entry, ordered by id. `id` is the URL id (`730` or `igdb:123`); `updated_at` is the latest offer fetch or subscription addition (sitemap `lastmod`). `page_size` defaults to 1000, max 5000.",
"parameters": [
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-5000. Default 1000.",
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfGameIndexPage"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
}
]
}
},
"/v1/games/{id}": {
"get": {
"tags": [
"Games"
],
"summary": "Game with offers, subscriptions, cheapest way to play and historical lows",
"description": "`id` is a Steam AppID (`730`) or an IGDB id (`igdb:123`). `time_to_beat_minutes` is the main story length (IGDB time to beat, hastily); null when unknown, for free-to-play, multiplayer-only and live-service games, or outside 2-100 hours. `is_free_to_play` marks games that cost nothing to start: they have no price and are left out of price lists. SteamSpy playtime is never used as main story. `other_region_offers` lists offers measured in other regions for editions with at most one offer measured in the visitor region; they are labelled with `measured_in` and never enter `cheapest_way` or the ranking.",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfGameDetailModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/games/{id}/history": {
"get": {
"tags": [
"Games"
],
"summary": "Authorized-store price history, daily minimum list price (before store fees)",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "days",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfHistoryPoint"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/under/{amount}": {
"get": {
"tags": [
"Discover"
],
"summary": "Games under $3, $5 or $10 (rounded to a round number in the requested currency)",
"description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does. `min_review` defaults to 75 here.",
"parameters": [
{
"name": "amount",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "sort",
"in": "query",
"description": "default, hours_per_dollar, discount, popularity or reviews.",
"schema": {
"type": "string"
}
},
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-100. Default 50.",
"schema": {
"type": "integer"
}
},
{
"name": "min_review",
"in": "query",
"description": "Minimum positive review percent, 0-100; 0 turns the filter off. Default 75.",
"schema": {
"type": "integer"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
},
{
"name": "hide_owned",
"in": "query",
"description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/tiers": {
"get": {
"tags": [
"Discover"
],
"summary": "Under tiers ($3, $5, $10) as applied in the requested currency",
"description": "`data` lists `usd` (3, 5 or 10) and `threshold` (Money: exactly the USD amount in USD; converted from USD and rounded to a round number in other currencies, e.g. TRY 100/200/400). The same amount filters /discover/under/{amount}. `euros` is a deprecated alias of `usd`.",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfDiscoverTierModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/historical-lows": {
"get": {
"tags": [
"Discover"
],
"summary": "Games at their historical low",
"description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
"parameters": [
{
"name": "sort",
"in": "query",
"description": "default, hours_per_dollar, discount, popularity or reviews.",
"schema": {
"type": "string"
}
},
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-100. Default 50.",
"schema": {
"type": "integer"
}
},
{
"name": "min_review",
"in": "query",
"description": "Minimum positive review percent, 0-100. Default: no filter.",
"schema": {
"type": "integer"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
},
{
"name": "hide_owned",
"in": "query",
"description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/leaving/{service}": {
"get": {
"tags": [
"Discover"
],
"summary": "Subscription games leaving within 30 days (game-pass, ps-plus)",
"description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
"parameters": [
{
"name": "service",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "sort",
"in": "query",
"description": "default, hours_per_dollar, discount, popularity or reviews.",
"schema": {
"type": "string"
}
},
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-100. Default 50.",
"schema": {
"type": "integer"
}
},
{
"name": "min_review",
"in": "query",
"description": "Minimum positive review percent, 0-100. Default: no filter.",
"schema": {
"type": "integer"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
},
{
"name": "hide_owned",
"in": "query",
"description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/play-with/{service}": {
"get": {
"tags": [
"Discover"
],
"summary": "Games you can play with a subscription (game-pass, ps-plus)",
"description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
"parameters": [
{
"name": "service",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "sort",
"in": "query",
"description": "default, hours_per_dollar, discount, popularity or reviews.",
"schema": {
"type": "string"
}
},
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-100. Default 50.",
"schema": {
"type": "integer"
}
},
{
"name": "min_review",
"in": "query",
"description": "Minimum positive review percent, 0-100. Default: no filter.",
"schema": {
"type": "integer"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
},
{
"name": "hide_owned",
"in": "query",
"description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/deal-of-the-day": {
"get": {
"tags": [
"Discover"
],
"summary": "Deal of the day for the region",
"description": "One game per region and UTC day, picked on the first request and stored (a game is not picked again for 7 days). Candidates: top-500 popularity, Steam reviews >= 85%, an authorized, fresh, discounted offer measured in the region, real price >= $2 (converted to the requested currency). Score is discount % x ln(1 + 500 / popularity rank). 404 when no game qualifies.",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDealModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/popular": {
"get": {
"tags": [
"Discover"
],
"summary": "Most popular games with a usable offer",
"description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does. Popularity order (Steam most played, wishlists, owners), free-to-play games excluded, only games with a fresh offer measured in the region. The list has a fixed short length (the home page chips); `page_size` can only shorten it.",
"parameters": [
{
"name": "sort",
"in": "query",
"description": "default, hours_per_dollar, discount, popularity or reviews.",
"schema": {
"type": "string"
}
},
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-100. Default 50.",
"schema": {
"type": "integer"
}
},
{
"name": "min_review",
"in": "query",
"description": "Minimum positive review percent, 0-100. Default: no filter.",
"schema": {
"type": "integer"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
},
{
"name": "hide_owned",
"in": "query",
"description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/discover/this-week": {
"get": {
"tags": [
"Discover"
],
"summary": "This week's picks",
"description": "Parameters: `sort` (default, hours_per_dollar, discount, popularity, reviews), `page` (from 1), `page_size` (1-100, default 50), `min_review` (0-100; 0 turns the filter off). `data` holds `rows`, `total`, `at_low_count`, `best_value`, `page` and `page_size`. On under lists `default` and `hours_per_dollar` order by hours per dollar while any row has a time-to-beat, and by real price (ascending) when none does.",
"parameters": [
{
"name": "sort",
"in": "query",
"description": "default, hours_per_dollar, discount, popularity or reviews.",
"schema": {
"type": "string"
}
},
{
"name": "page",
"in": "query",
"description": "Page number from 1 (at most 1000).",
"schema": {
"type": "integer"
}
},
{
"name": "page_size",
"in": "query",
"description": "Rows per page, 1-100. Default 50.",
"schema": {
"type": "integer"
}
},
{
"name": "min_review",
"in": "query",
"description": "Minimum positive review percent, 0-100. Default: no filter.",
"schema": {
"type": "integer"
}
},
{
"name": "owned",
"in": "query",
"description": "Comma-separated subscription codes the caller already owns (e.g. `gp_pc,psplus_extra`). An owned subscription costs nothing, so a game in its catalog is \"already included\" instead of a buy recommendation. Nothing is stored. Unknown codes are rejected.",
"schema": {
"type": "string"
}
},
{
"name": "hide_owned",
"in": "query",
"description": "With `owned`: true leaves out games that are already included in an owned subscription. Default false.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfDiscoverPageModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/subscriptions": {
"get": {
"tags": [
"Subscriptions"
],
"summary": "Subscription summaries by region",
"description": "Price per month in the region, catalog size (all games we list, the same number the site shows everywhere), new this week and leaving within 30 days.",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfSubscriptionSummaryModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/subscriptions/{code}/catalog": {
"get": {
"tags": [
"Subscriptions"
],
"summary": "Subscription catalog by region",
"parameters": [
{
"name": "code",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfDealModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/HttpValidationProblemDetails"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/stores": {
"get": {
"tags": [
"Stores"
],
"summary": "Stores with type, fee rules and verification dates",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfStoreView"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/meta/status": {
"get": {
"tags": [
"Meta"
],
"summary": "Source health: last successful fetch, age, consecutive failures and whether it is overdue (no key needed)",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfSourceStatus"
}
}
}
}
},
"security": [
{
"ApiKey": [ ]
},
{ }
]
}
},
"/v1/keys": {
"post": {
"tags": [
"Keys"
],
"summary": "Create a developer API key",
"description": "Returns the key once, in plain text. Only its SHA-256 hash is stored. Limited to 3 keys per IP per day.",
"requestBody": {
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "#/components/schemas/CreateKeyRequest"
}
]
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreatedKeyModel"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/co
# --- truncated at 32 KB (109 KB total) ---
# Full source: https://artifacts.apis.io/fairly-gg/openapi/_original/fairly-gg-api-openapi.json