Concepts

FreeStuff's API is in large parts merely a read only gateway to our Content Management System (CMS). In order to understand all the things the API exposes you need to understand how our CMS is constructed.

Don't worry, it's not complex.

Products

At the core of everything are products. A product is a free game, an ingame item in gamepass, or anything else the bot sends notifications for.

Each product is part of an Announcement and has both a Channel as it's type and a ProductKind as it's kind.

Additionally a product has some basic properties like a title, assets (logos, images), and description, it has pricing information about old and new prices in multiple currencies, as well as metadata in various forms. See the corresponding Data Model for a full list of properties.

Each product has a kind as described earlier. This value describes what kind of product it is. Usually it's a game, but others like dlc or loot for game pass or prime gaming are also possible. FreeStuff also has more types like art, ost, or other which are currently unused.

The Data Models below show compatibility date 2025-03-01. Newer versions might differ in details.
Product: ObjectData Model
{
id: int
title: string
prices: ProductPrice[]
kind: ProductKind
tags: string[] unsorted, non-standardized, human readable
images: ProductImage[] *this array will only have a subset of all available images on the free tier
description: string human readable description in english language
rating: float this is an aggregated score where 0 represents the worst possible and 1 the best possible rating
copyright: string name of the ip copyright holder
until: int unix timestamp in ms
type: PublishingChannel
urls: ProductUrl[] *this array will only have a subset of all available urls on the free tier
store: Store steam, epic games, etc.
flags: ProductFlags
notice: string a rare optional extra info text added by a FreeStuff team member meant for public display alongside the product
staffApproved: boolean true means a human checked the data prior to being published
*platforms: Platform[] *this array will always be empty on the free tier
*meta: ProductMeta[] *this array will always be empty on the free tier
}
PartialProduct: ObjectData Model
{
id: int
kind: ProductKind
until: int unix timestamp in ms
type: PublishingChannel
flags: ProductFlags
store: Store steam, epic games, etc.
}
ProductPrice: ObjectData Model
{
currency: string currency code as by https://www.iban.com/currency-codes
oldValue: int price before the discount; in the currency's smallest unit, e.g. cents; a value of 499 in usd would correspond to $4.99
newValue: int price after the discount; in the currency's smallest unit; usually 0 if product is free
converted: boolean if true the value was converted from either the product's EUR or USD price using market conversion rates; if false the value was taken from the store directly and is usually more accurate
}
ProductKind: stringData Model
game normal games
dlc game addons
loot ingame content
software non-game software (unused)
art digital artwork (unused)
ost game soundtrack (unused)
book digital book (unused)
storeitem things like steam points store content
other other kinds of content, gaming related
ProductImage: ObjectData Model
{
url: string
flags: ProductImageFlags
priority: int a number indicating how well a certain image is suited for presentation; higher is better
}
ProductImageFlags: int (bitfield)Data Model
1<<0 (PROXIED) if this image is proxied and/or provided by FreeStuff
1<<1 (AR_WIDE) aspect ratio: image is in landscape orientation
1<<2 (AR_SQUARE) aspect ratio: image is square
1<<3 (AR_TALL) aspect ratio: image is in portrait orientation
1<<4 (TP_PROMO) type: promotional image
1<<5 (TP_LOGO) type: product's logo
1<<6 (TP_SHOWCASE) type: showcase screenshot (e.g. ingame screenshot)
1<<7 (TP_OTHER) type: other, any other kind of image
1<<8 (FT_WATERMARK) feature: the image has a FreeStuff watermark on it
1<<9 (FT_TAGS) feature: the image has the tags rendered onto it
note: All flags are optional. Additional internal flags exist that are not documented here - your implementation must be able to handle them.
ProductUrl: ObjectData Model
{
url: string
flags: ProductUrlFlags
priority: int a number indicating how well a certain url is suited for use; higher is better
}
ProductUrlFlags: int (bitfield)Data Model
1<<0 (ORIGINAL) only the original link gets this flag
1<<1 (PROXIED) the url is proxied through FreeStuff or a thid party
1<<2 (TRACKING) the url is tracking how many times it has been clicked. No pii is collected.
1<<3 (OPENS_IN_BROWSER) the url opens the platform's website
1<<4 (OPENS_IN_CLIENT) the url opens in the platform's client applications
note: All flags are optional. Additional internal flags exist that are not documented here - your implementation must be able to handle them.
Store: stringData Model
other
steam
epic
humble
gog
origin
ubi
itch
prime
Please note that this list is non-exhaustive! While the names above will not change, new stores may be added at will and this documentation might not always be up to date with all possible values. Be sure to handle unknown platform values properly, e.g. treating them as 'other' until further differenciated.
Platform: stringData Model
windows
mac
linux
android
ios
xbox
playstation
Please note that this list is non-exhaustive! While the names above will not change, new platforms may be added at will and this documentation might not always be up to date with all possible values. Be sure to handle unknown platform values properly, e.g. ignoring them.
ProductFlags: int (bitfield)Data Model
1<<0 (TRASH) game is filtered out by the low quality setting
1<<1 (THIRDPARTY) game is offered trough a thirdparty site (deprecated)
1<<2 (PERMANENT) the offer is permanent
1<<3 (STAFF_PICK) the offer is marked as a "staff pick"
1<<4 (FIRSTPARTY_EXCLUSIVE) the offer is only shown on FreeStuff's official channels. You should never receive a product with this flag. If you do, please ignore it.
note: All flags are optional. Additional internal flags exist that are not documented here - your implementation must be able to handle them.
ProductMeta: ObjectData Model
{
key: ProductMetaKey
value: string depending on the key, certain metadata may be of numeric or boolean form. These values are still encoded and delivered as JSON compatible strings. E.g. "true", "false", "12", "-4e8"
}
ProductMetaKey: stringData Model
slug unique id on the product's store
scraper.sources the sources used by the webscraper
scraper.version the version of the webscraper used
igdb.gameid id of the product's corresponding game in the igdb
steam.subids steam subids, if any, comma seperated
steam.achievements steam achievement count
steam.recommendations steam number of curator recommendations
epic.namespace epic games namespace
epic.id epic games id
itch.creator itch.io creator name
prime.itemid prime itemid
prime.offerid prime offerid
prime.codegrant prime does this product grant a code?
prime.direct prime is this product a direct claim?
prime.linkitem prime is this product a retail link item?
prime.fgwp prime is product flagged as Free Games With Prime?
prime.priority prime listing priority
note: This list is non-exhaustive and only shows commonly used keys. Additional metadata may be present. Fields starting with a store name are only present on products coming from this store.
ProductApprovalStatus: stringData Model
approved the product was approved by a human content moderator or automated system but is not yet published
published this product has been published and is visible
expired this offer has expired and is no longer visible or accessible
Please note that other internal statuses exist. You should never accidentally come accross a product with such status but your implementation should be able to handle them by e.g. treating all products with unknown status as not found.

Announcements

When we publish products to send to our official bots on Discord, Telegram and potentially others, as well as our API users like you, we're not publishing products individually. Instead we often bundle multiple products together because they have semantic relevance and/or because it allows us to more effectively distribute the products to our hundreds of thousands of subscribers.

An announcement describes such a collection of products. Every time we publish something a new announcement is created, even if it's just for a single product.

Each announcement goes into a Channel. All products belonging to that announcement also have the same Channel set as their type.

Announcement: ObjectData Model
{
id: number
products: int[] ids of all products in the announcement
}
ResolvedAnnouncement: ObjectData Model
{
id: number
products: int[] ids of all products in the announcement
resolvedProducts: Product[] resolved products
}

Channels

FreeStuff supports different kinds of free games. There's games that have a temporary 100% discount, games that are available to play for a limited amount of time without you owning them, there's game pass and prime gaming, there's DLCs and additional content, etc.

Each of those is a channel. You might only be interested in a single one of those channels or you might be interested in multiple or all of them.

More channels may be added in the future but here is our current list:

Channel: stringData Model
keep Free to Keep / 100% discount
timed Free to play / Free weekend
other DLCs & More / everything else
prime Available through Prime Gaming
gamepass Available through GamePass
mobile not for public use
news not for public use
unknown not for public use
debug not for public use

Content Summary

FreeStuff has different Channels with different purposes.

A Channel is used to publish Announcements.

An Announcement is a collection of Products, all with the same type but of potentially different kinds.

Compatibility Dates

If you are using a client library you do not need to worry about compatibility dates. Your library will handle all of this for you and use a version that it is compatible with.

API v2 introduces compatibility dates. These act as versioning for the format of the data you can access through FreeStuff's API.

In simpler terms: Requesting a product through different compatibility dates will always return the same product but the data format might change. For example, an older version might not include the product's description, while another version might change some field's name from "title" to "product_name".

We made this decision to allow our data model to evolve and API users to make use of this without introducing a new API version every time.

REST endpoints and the overarching structure of webhook deliveries will never have any breaking changes under v2 but compatibility dates allow our datamodel to have such.

A compatibility date always follows the format YYYY-MM-DD and should generally be set to the date you start working on your API integration. Avoid picking dates in the past as documentation might not reflect older data models. While theoretically possible, never pick compatibilty dates from the future as this might deliver data in a potentially unknown format, which is usually not what you want as breaking changes can be introduced any moment this way.

While the REST API and Webhooks handle setting/configuring the compatibility date differently, the data retrieved will always be in the same format for the same compatibility date, regardless of the interface it is accessed through.