How to use iapPriceSchedule?

I'm trying to figure out how to fetch the price schedules for my in-app purchases using the app store connect API: https://developer.apple.com/documentation/appstoreconnectapi/read_the_price_schedule_for_an_in-app_purchase

Using the api endpoints in the docs, I can get a list of all schedules (for every in app purchase in the whole app), as well as a list of all price points – but there doesn't seem to be a way to associate them with the specific in app purchase, or to figure out which schedule is associated with which price tier.

Here's the endpoints I'm using, ad examples of the responses I'm getting: (in this data {id} is the id of the specific in app purchase, and {product_id} is the product id that was used to create it)

v1/apps/{settings.ITUNES_APPLE_ID}/inAppPurchasesV2 with the parameters

{
    'filter[productId]': {{product_id}},
    'fields[inAppPurchasePriceSchedules]': 'inAppPurchase'
}

gets a response that looks like this:

{
  "data": [
    {
      "type": "inAppPurchases",
      "id": "xxxx",
      "attributes": {
        "name": "***",
        "productId": "xxxx",
        "inAppPurchaseType": "NON_CONSUMABLE",
        "state": "APPROVED",
        "reviewNote": null,
        "familySharable": false,
        "contentHosting": false,
        "availableInAllTerritories": true
      },
      "relationships": {
        "pricePoints": {
          "links": {
            "self": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/relationships/pricePoints",
            "related": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/pricePoints"
          }
        },
        "content": {
          "links": {
            "self": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/relationships/content",
            "related": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/content"
          }
        },
        "iapPriceSchedule": {
          "links": {
            "self": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/relationships/iapPriceSchedule",
            "related": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/iapPriceSchedule"
          }
        }
      },
      "links": {
        "self": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}"
      }
    }
  ],
  "links": {
    "self": "https://api.appstoreconnect.apple.com/v1/apps/{id}/inAppPurchasesV2?filter%5BproductId%5D={product_id}&fields%5BinAppPurchasePriceSchedules%5D=inAppPurchase"
  },
  "meta": {
    "paging": {
      "total": 1,
      "limit": 50
    }
  }
}

fetching the iap from https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id} gets a very similar response

Fetching the price schedules from https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/iapPriceSchedule gets something like this:

{
  "data": {
    "type": "inAppPurchasePriceSchedules",
    "id": {id},
    "relationships": {
      "manualPrices": {
        "links": {
          "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}/relationships/manualPrices",
          "related": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}/manualPrices"
        }
      }
    },
    "links": {
      "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}"
    }
  },
  "links": {
    "self": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/iapPriceSchedule"
  }
}

getting the manual prices from https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}/manualPrices returns something like this:

{
  "data": [
    {
      "type": "inAppPurchasePrices",
      "id": "eyJpIjoiMTM0Njg1ODA5NSIsImQiOjE3NTcwLCJjIjoiVFVSIn0",
      "attributes": {
        "startDate": "2018-02-08"
      },
      "links": {
        "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePrices/eyJpIjoiMTM0Njg1ODA5NSIsImQiOjE3NTcwLCJjIjoiVFVSIn0"
      }
    },
    {
      "type": "inAppPurchasePrices",
      "id": "eyJpIjoiMTM0Njg1ODA5NSIsImQiOjE3NTcwLCJjIjoiTEJZIn0",
      "attributes": {
        "startDate": "2018-02-08"
      },
      "links": {
        "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePrices/eyJpIjoiMTM0Njg1ODA5NSIsImQiOjE3NTcwLCJjIjoiTEJZIn0"
      }
    },
    ... 48 more
  ],
  "links": {
    "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}/manualPrices",
    "next": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}/manualPrices?cursor=Mg.QxIT0w&limit=50"
  },
  "meta": {
    "paging": {
      "total": 8575,
      "limit": 50
    }
  }
}

fetching the price points from https://api.appstoreconnect.apple.com/v2/inAppPurchases/{id}/pricePoints returns something like this:

{
  "data": [
    {
      "type": "inAppPurchasePricePoints",
      "id": "MTM0Njg1ODA5NV9id18w",
      "attributes": {
        "customerPrice": "0.0",
        "proceeds": "0.0",
        "priceTier": "0"
      },
      "links": {
        "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePricePoints/MTM0Njg1ODA5NV9id18w"
      }
    },
    {
      "type": "inAppPurchasePricePoints",
      "id": "MTM0Njg1ODA5NV9kbV8w",
      "attributes": {
        "customerPrice": "0.0",
        "proceeds": "0.0",
        "priceTier": "0"
      },
      "links": {
        "self": "https://api.appstoreconnect.apple.com/v1/inAppPurchasePricePoints/MTM0Njg1ODA5NV9kbV8w"
      }
    },
    ... 48 more
  ],
  "links": {
    "self": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/1346858095/pricePoints",
    "next": "https://api.appstoreconnect.apple.com/v2/inAppPurchases/1346858095/pricePoints?cursor=Mg.NkAOYA&limit=50"
  },
  "meta": {
    "paging": {
      "total": 16625,
      "limit": 50
    }
  }
}

GETing the individual links for inAppPurchasePrices and inAppPurchasePricePoints nets me a 403

I can't for the life of me figure out how to stitch these together into something useful. Is there something I'm missing here? It should be possible to get this info from this api, right?

We've been using the old xml uploader up to now, but got an email that it's going to be EOL'd in November.

Here is the curl to help with fetching inAppPurchasePriceSchedules:

`curl "https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/:id/manualPrices?filter%5Bterritory%5D=CHN,CAN,SVN,COL&include=inAppPurchasePricePoint" \
     -H 'Content-Type: application/json' \
     -H 'Authorization`

Please see this link for additional information:  https://developer.apple.com/documentation/appstoreconnectapi/read_price_information_for_an_in-app_purchase_price_schedule

Accepted Answer

This API is VERY confusing, but it's not entirely broken.

First, you might want to review this WWDC video from 2020, which explains how app prices work. https://developer.apple.com/videos/play/wwdc2020/10004/

Note that a "price point" is a mapping from price tiers (there are 95 of those) to territories (175 of those), and defines the localized price for that tier in that territory. Thus there are 95 x 175 = 16,625 price points as I write this comment.

Unfortunately, the IAP price API works very differently from the API for full-app prices documented at WWDC 2020. 😖

The big difference is that the /v1/apps/:appId/prices endpoint allows you to ?include=priceTier to see the tier of the price. IAP prices don't do that; inAppPurchasePriceSchedules links directly to inAppPurchasePrices, each of which only includes a start date and a relationship to its price point. Therefore, you're gonna see 175 inAppPurchasePrices for each entry in the price schedule!

Worse, the API doesn't actually show the territory for a price point unless you explicitly request to include it, so the API results initially look like nonsense.

For example, the following request will simply return all 16,625 possible available price points.

https://api.appstoreconnect.apple.com/v2/inAppPurchases/:iapId/pricePoints

But worse, most of the points in data look indistinguishable, because the price points don't mention their territory. This request is actually understandable:

https://api.appstoreconnect.apple.com/v2/inAppPurchases/:iapId/pricePoints?include=territory

When you do that, you'll be able to see the territory associated with each price point.

OK, so, how do you get the schedule for just one IAP? That's documented here: https://developer.apple.com/documentation/appstoreconnectapi/read_price_information_for_an_in-app_purchase_price_schedule (You did manage to find that on your own!)

You're meant to read the "manual prices" for using the IAP's Apple ID.

But to get a useful view of the "manual prices" price schedule, you'll have to include the price points and the territory on every request.

https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/:iapId/manualPrices?include=inAppPurchasePricePoint,territory

That will include all price points for all territories for all time for that IAP. For my IAP, which had changed prices only once, the API wanted to return me 525 inAppPurchasePrices objects, each of which linked to 350 inAppPurchasePricePoints for 175 territories. (Without include=inAppPurchasePricePoint, you only see the start dates repeated 175 times per calendar entry, and without include=territory, you can't see which price point is which!)

But who needs 175 price points per schedule entry?! You almost certainly just wanted the price tier for each price schedule entry. So, instead, try filtering by a single territory, USA (and now we can skip including territory because we know they're all the same territory):

https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/:iapId/manualPrices?include=inAppPurchasePricePoint&filter[territory]=USA

For me, that returned just three inAppPurchasePrices objects, one with startDate: null (which, as the WWDC video explains, is the current, actual price) and two with real start dates. In included, I see two associated price points, including a customerPrice in USA dollars and a priceTier. (The current (null) price and the latest entry on the schedule point to the same price point.)

Annoyingly, Apple provides no way to pass a filter argument for startDate=null, so if you want the current latest price, you'll just have to filter out the inAppPurchasePrices with non-null start dates yourself on the client side.

I hope this helps! (It took me all day to figure it out.)

How to use iapPriceSchedule?
 
 
Q