---
title: "Subscription rotation plan: the smartest way to play a list of games"
method: POST
path: "/v1/plan"
tags: ["Plan"]
---

# Subscription rotation plan: the smartest way to play a list of games

`POST /v1/plan`

## Request

- Base URL: `https://api.fairly.gg`
- URL: `https://api.fairly.gg/v1/plan`
- Auth: one of:
  - API key in header `X-Api-Key`
  - none

Month-by-month plan for up to 30 games: buy at the cheapest measured real price, or play inside a subscription month while the game is in the catalog. Honors weekly hours (default 10, a subscription month is 4 weeks), announced leave dates and the subscriptions you already own (`owned_subscriptions`, marginal cost 0). Totals compare against buying every game at its cheapest measured price, never list price. Games without a reliable main-story length (free to play, live service, multiplayer only, missing) are left out of every time-based calculation and listed in `excluded`. Future months assume a game stays in its catalog unless a leave date is known (`assumes_catalog_stays`). `assumptions` repeats the inputs; every price carries `buy_price_fetched_at`. No API key needed; the keyless per-IP limits apply. Nothing is stored.

## Request body

- PlanRequestBody
  - `games` string[], nullable, required
  - `owned_subscriptions` string[], nullable, required
  - `hours_per_week` number, double, nullable, required
  - `region` string, nullable, required
  - `currency` string, nullable, required
  - `include_marketplaces` boolean, nullable, required

## Response `200`

OK

- ApiResponseOfPlanResult
  - `data` PlanResult, required
    - `months` PlannedMonth[], required
      - `index` integer, required
      - `starts_at` string, date-time, required
      - `paid_subscription` PlanSubscription, required
        - `code` string, required
        - `name` string, required
        - `monthly_price` Money, required
          - `amount` number, required
          - `currency` string, required: USD, EUR, GBP or TRY.
          - `formatted` string, required: Display text, e.g. "€9.99".
      - `finishing_game_ids` integer[], required
    - `games` PlannedGame[], required
      - `game_id` integer, required
      - `title` string, required
      - `source` 'buy' | 'owned_subscription' | 'paid_subscription', required
      - `subscription_code` string, nullable, required
      - `play_minutes` integer, required
      - `first_month` integer, required
      - `finish_month` integer, required
      - `cost` Money, required
        - `amount` number, required
        - `currency` string, required: USD, EUR, GBP or TRY.
        - `formatted` string, required: Display text, e.g. "€9.99".
      - `buy_store` string, nullable, required
      - `buy_offer_id` integer, nullable, required
      - `buy_price_fetched_at` string, date-time, nullable, required
      - `leaving_at` string, date-time, nullable, required
      - `assumes_catalog_stays` boolean, required
      - `game` GameSummary
        - `id` integer, required
        - `steam_app_id` integer, nullable, required
        - `igdb_id` integer, nullable, required
        - `title` string, required
        - `slug` string, required
        - `cover_url` string, nullable, required
        - `cover_portrait_url` string, nullable
    - `excluded` PlanExcluded[], required
      - `game_id` integer, required
      - `title` string, required
      - `reason` 'free_to_play' | 'no_play_time' | 'no_way_to_play', required
      - `buy_price` Money, required
        - `amount` number, required
        - `currency` string, required: USD, EUR, GBP or TRY.
        - `formatted` string, required: Display text, e.g. "€9.99".
      - `game` GameSummary
        - `id` integer, required
        - `steam_app_id` integer, nullable, required
        - `igdb_id` integer, nullable, required
        - `title` string, required
        - `slug` string, required
        - `cover_url` string, nullable, required
        - `cover_portrait_url` string, nullable
    - `totals` PlanTotals, required
      - `plan` Money, required
        - `amount` number, required
        - `currency` string, required: USD, EUR, GBP or TRY.
        - `formatted` string, required: Display text, e.g. "€9.99".
      - `buy_everything` Money, required
        - `amount` number, required
        - `currency` string, required: USD, EUR, GBP or TRY.
        - `formatted` string, required: Display text, e.g. "€9.99".
      - `savings` Money, required
        - `amount` number, required
        - `currency` string, required: USD, EUR, GBP or TRY.
        - `formatted` string, required: Display text, e.g. "€9.99".
      - `buy_everything_excludes_game_ids` integer[], required
    - `assumptions` PlanAssumptions, required
      - `hours_per_week` number, double, required
      - `hours_per_month` number, double, required
      - `weeks_per_month` integer, required
      - `play_time_source` string, required
      - `subscriptions` PlanSubscription[], required
        - `code` string, required
        - `name` string, required
        - `monthly_price` Money, required
          - `amount` number, required
          - `currency` string, required: USD, EUR, GBP or TRY.
          - `formatted` string, required: Display text, e.g. "€9.99".
      - `owned_codes` string[], required
  - `fetched_at` string, date-time, required
  - `data_age_seconds` integer, required
  - `stale` boolean, required
  - `region` unknown
  - `currency` string, nullable
  - `region_fallback` boolean, nullable
  - `attribution` string

## Other responses

- `400`: Bad Request
- `404`: Not Found

---

[API](https://skmtc.dev/fairly/apis/fairly-api.md) · [All operations](https://skmtc.dev/fairly/apis/fairly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fairly/apis/fairly-api/revisions/9b21c64dffa2?raw)
