Sales

The /reports/sales resource provides comprehensive access to sales reports. Reports can be generated in a variety of formats.

The main resource gives you a single combined result across the filter parameters you select. The result can be limited by dates, products, countries and some other parameters.

Using the `group_by` parameter, you can get a report broken down by products, countries, dates, stores, or any combination thereof.

Scope: All of the resources in this route require read access in the private scope (private:read).

Getting “All Time” totals

A very common use case is to get all the data for an app and add it up for an “All time total”. If you don’t provide start_date and end_date and aren’t using a group_by (for the routes that accept a group_by) that includes ‘dates’ you’ll get a sum of all the available data.

For some products, such as free apps from the Android Market, this will give complete “all time” data set, and not just for the data that’s been imported into the Appfigures account. It may not agree with actually summing up individual days because of inconsistencies in Google’s data.

Getting Simple Totals

Request

GET /reports/sales/?group_by={group_by}&start_date={start_date}&end_date=(end_date)&granularity={granularity}&products={product_ids}&countries={countries}&dataset={dataset}&include_inapps={include_inapps}&format={format}

Arguments

start_date
(yyyy-MM-dd) Date, inclusive, to start reporting from. If you specify a granularity other than daily this will be rounded down to the nearest period. default: a long, long time ago.


end_date
(yyyy-MM-dd) Date, inclusive, to stop reporting. If you specify a granularity other than daily this will be rounded up to the nearset period. default: distant future.


group_by
One or more pivots separated by a comma (,). Available pivots: product, country, date, and store.


granularity
(daily | weekly | monthly | yearly) How to group dates. This affects how start_date and end_date are rounded (they snap to lower and upper barriers of the type given by this parameter) default: daily


Note: the daily and weekly granularities are limited to a maximum of 30 and 365 days per request respectively.

products
(productId | productId1;productId2;productIdN) specific products to include in the response, default: all products in account


countries
(isoCode | isoCode1;isoCode2;isoCodeN) Countries to limit the report to (can be taken from /data/countries, default: include all countries)


dataset
(none | financial ) If this is set to financial, use financial reports. This only applies to Apple products, default: none


include_inapps
(true | false) If set to true any products given in product_ids will have their children automatically selected as well. default: false)


format
Output format: JSON or CSV. Defaults to JSON.

Note: To prevent excessive repetition, when format=csv sub-object (like the product in /sales/products/…) the object will not be fully serialized but only its reference will be.

Response

downloads An int representing the total number of downloads.
net_downloads An int representing the number of downloads – returns.
updates An int representing the total number of updates.
revenue A float representing the total revenue in the user’s selected currency.
returns An int representing the total number of returns.
gifts An int representing the number of times products in this report were gifted.
gift_redemptions An int representing the number of times products in this report were gifted and then redeemed.
promos An int representing the total number of promo codes used.
edu_downloads An int representing the total number of educational downloads.

Example

// GET /reports/sales/
{
    downloads: 155299,
    updates: 91156,
    returns: 56,
    net_downloads: 155243,
    promos: 2,
    revenue: "26188.63",
    edu_downloads: 54,
    gifts: 36,
    gift_redemptions: 0
}

Generating a Report By Product, Country, or Date

Request

GET /reports/sales/?group_by={group_by}&start_date={start_date}&end_date=(end_date)&granularity={granularity}&products={product_ids}&countries={countries}&dataset={dataset}&include_inapps={include_inapps}&format={format}

Arguments

start_date
(yyyy-MM-dd) Date, inclusive, to start reporting from. If you specify a granularity other than daily this will be rounded down to the nearest period. default: a long, long time ago.


end_date
(yyyy-MM-dd) Date, inclusive, to stop reporting. If you specify a granularity other than daily this will be rounded up to the nearset period. default: distant future.


group_by
One or more pivots separated by a comma (,). Available pivots: product, country, date, and store.


granularity
(daily | weekly | monthly | yearly) How to group dates. This affects how start_date and end_date are rounded (they snap to lower and upper barriers of the type given by this parameter) default: daily


Note: the daily and weekly granularities are limited to a maximum of 30 and 365 days per request respectively.

products
(productId | productId1;productId2;productIdN) specific products to include in the response, default: all products in account


countries
(isoCode | isoCode1;isoCode2;isoCodeN) Countries to limit the report to (can be taken from /data/countries, default: include all countries)


dataset
(none | financial ) If this is set to financial, use financial reports. This only applies to Apple products, default: none


include_inapps
(true | false) If set to true any products given in product_ids will have their children automatically selected as well. default: false)


format
Output format: JSON or CSV. Defaults to JSON.

Note: To prevent excessive repetition, when format=csv sub-object (like the product in /sales/products/…) the object will not be fully serialized but only its reference will be.

Note: specifying a group_by that includes date without a start or end will default the start and end dates to the past 30 days.

Response

The response depends on the specified group_by given. No matter which group_by, the deepest part of the representation is the following leaf:

downloads An int representing the total number of downloads.
net_downloads An int representing the number of downloads – returns.
updates An int representing the total number of updates.
revenue A float representing the total revenue in the user’s selected currency.
returns An int representing the total number of returns.
gifts An int representing the number of times products in this report were gifted.
gift_redemptions An int representing the number of times products in this report were gifted and then redeemed.
promos An int representing the total number of promo codes used.
edu_downloads An int representing the total number of educational downloads.
date (optional) A string (yyyy-MM-dd) representing the date of the leaf. This is only returned when the `group_by` includes `date`.
product_id (optional) The product’s ID. This is only returned when the `group_by` includes `product`.
iso (optional) The ISO code for the current leaf’s country. This is only returned when the `group_by` includes `country`.
country (optional) The leaf’s country name. This is only returned when the `group_by` includes `country`.
product (optional) A full representation of the leaf’s product. This is only returned when the `group_by` includes `product`.

Example Response For Sales By Date Report

// GET /reports/sales/?group_by=dates

{
  "2013-05-28": {
    "downloads": 233,
    "updates": 2,
    "returns": 0,
    "net_downloads": 233,
    "promos": 0,
    "revenue": "246.97",
    "edu_downloads": 0,
    "gifts": 0,
    "gift_redemptions": 0,
    "date": "2013-05-28"
  },
        . . .
  "2013-06-27": {
    "downloads": 64,
    "updates": 0,
    "returns": 0,
    "net_downloads": 64,
    "promos": 0,
    "revenue": "99.48",
    "edu_downloads": 0,
    "gifts": 0,
    "gift_redemptions": 0,
    "date": "2013-06-27"
  }
}

Example For Sales By Country Report

// GET /reports/sales/?group_by=countries


{
  "AE": {
    "downloads": 840,
    "updates": 271,
    "returns": 0,
    "net_downloads": 840,
    "promos": 0,
    "revenue": "268.28",
    "edu_downloads": 0,
    "gifts": 0,
    "gift_redemptions": 0,
    "country": "UAE",
    "iso": "AE"
  },
  "AG": {
    "downloads": 4,
    "updates": 1,
    "returns": 0,
    "net_downloads": 4,
    "promos": 0,
    "revenue": "2.30",
    "edu_downloads": 0,
    "gifts": 0,
    "gift_redemptions": 0,
    "country": "Antigua And Barbuda",
    "iso": "AG"
  },
  . . .
}

Example For Sales By Product Report

// GET /reports/sales/?group_by=products&start=2010-05-20&end=2010-05-25

{
  "12345": {
    "downloads": 45,
    "updates": 0,
    "returns": 45,
    "net_downloads": 0,
    "promos": 0,
    "revenue": "0.64",
    "edu_downloads": 0,
    "gifts": 0,
    "gift_redemptions": 0,
    "product_id": 12345,
    "product": {
      "id": 12345,
      "name": "Minimum Viable App",
      "developer": "Jeff",
      "icon": "https://lh3.ggpht.com/jeffs-icon",
      "vendor_identifier": "com.jeff.mva",
      "package_name": "com.jeff.mva",
      "store_id": 2,
      "store": "google_play",
      "release_date": "2000-01-02T00:00:00",
      "added_date": "2011-12-01T20:22:05",
      "updated_date": "2013-06-04T03:00:00",
      "version": "4.0.2",
      "source": {
        "external_account_id": 77,
        "added_timestamp": "2012-02-27T19:09:36",
        "active": true,
        "hidden": false,
        "type": "own"
      },
      "type": "unknown",
      "devices": [
        "Handheld"
      ],
      "children": [],
      "features": [],
      "parent_id": null
    }
  },
  . . .
}

Example For Sales By Product and Date Report

Response: structure of products mapped to dates mapped to sales information

// GET /reports/sales/products+dates?start_date=2010-11-24end_date=2010-11-31

{
  "12345": {
    "2010-11-24": {
      "date": "2010-11-24",
      "country": null,
      "iso": null,
      "product_id": 12345,
      "downloads": 11,
      "net_downloads": 11,
      "updates": 0,
      "revenue": "0.00",
      "returns": 0,
      "gift_redemptions": 0,
      "promos": 0
    },
    ...
  },
  "123456": {
    "2010-11-24": {
      "date": "2010-11-24",
      "product_id": 123456,
      "downloads": 28,
      "net_downloads": 29,
      "updates": 6,
      "revenue": "19.02",
      "returns": 1,
      "gift_redemptions": 0,
      "promos": 0
    },
    ...
  }
}

Example For Sales By Date and Product Report

Response: structure of dates mapped to products mapped to sales information

// GET /reports/sales/dates+products?start_date=2010-11-24&end_date=2010-11-31

{
  "2010-11-24": {
    "12345": {
      "date": "2010-11-24",
      "product_id": 12345,
      "downloads": 11,
      "net_downloads": 11,
      "updates": 0,
      "revenue": "0.00",
      "returns": 0,
      "gift_redemptions": 0,
      "promos": 0
    },
    ...
  },
  "2010-11-25": {
    "294956": {
      "date": "2010-11-25",
      "product_id": 123456,
      "downloads": 10,
      "net_downloads": 10,
      "updates": 1,
      "revenue": "0.00",
      "returns": 0,
      "gift_redemptions": 0,
      "promos": 0
    },
    ...
  },
  ...
}

Generating a By Region Sales Report

The sales by region report is only available for iTunes Connect accounts at this time.

Request

GET /reports/sales/regions/?start_date={start_date}&end_date=(end_date)&granularity={granularity}&products={product_ids}&countries={countries}&dataset={dataset}&include_inapps={include_inapps}&format={format}

Arguments

start_date
(yyyy-MM-dd) Date, inclusive, to start reporting from. If you specify a granularity other than daily this will be rounded down to the nearest period. default: a long, long time ago.


end_date
(yyyy-MM-dd) Date, inclusive, to stop reporting. If you specify a granularity other than daily this will be rounded up to the nearset period. default: distant future.


group_by
One or more pivots separated by a comma (,). Available pivots: product, country, date, and store.


granularity
(daily | weekly | monthly | yearly) How to group dates. This affects how start_date and end_date are rounded (they snap to lower and upper barriers of the type given by this parameter) default: daily


Note: the daily and weekly granularities are limited to a maximum of 30 and 365 days per request respectively.

products
(productId | productId1;productId2;productIdN) specific products to include in the response, default: all products in account


countries
(isoCode | isoCode1;isoCode2;isoCodeN) Countries to limit the report to (can be taken from /data/countries, default: include all countries)


dataset
(none | financial ) If this is set to financial, use financial reports. This only applies to Apple products, default: none


include_inapps
(true | false) If set to true any products given in product_ids will have their children automatically selected as well. default: false)


format
Output format: JSON or CSV. Defaults to JSON.

Note: To prevent excessive repetition, when format=csv sub-object (like the product in /sales/products/…) the object will not be fully serialized but only its reference will be.

Response

region_id The numeric ID of the region.
region_name Full name of the region.
currency The region’s original currency.
returns Number of returns in the region.
units Number of units sold in the region.
base_revenue Total revenue in the region’s currency.
converted_revenue Total revenue converted into the user’s currency.

Example Response

// GET /reports/sales/regions

{
  "Australia": {
      "region_id": 1,
      "region_name": "Australia",
      "currency": "AUD",
      "returns": 0,
      "units": 108,
      "converted_revenue": "64.87",
      "base_revenue": "65.36"
  },
  "Canada": {
      "region_id": 2,
      "region_name": "Canada",
      "currency": "CAD",
      "returns": 0,
      "units": 32,
      "converted_revenue": "15.94",
      "base_revenue": "16.10"
  },
  ...
}
  • Alicia Majcherczyk

    Hello! I’m trying to access the data from a customer’s account, through the API. I have all information and it seems that I have all accesses to the data. However, when running the reports/sales request, I have the result below, with every metrics equals to 0. Do you have any idea of what I’m doing wrong?

    Thanks a lot in advance

    {

    “downloads”: 0,

    “re_downloads”: 0,

    “updates”: 0,

    “returns”: 0,

    “net_downloads”: 0,

    “promos”: 0,

    “revenue”: “0.00”,

    “returns_amount”: “0.00”,

    “edu_downloads”: 0,

    “gifts”: 0,

    “gift_redemptions”: 0

    }

  • Desiree’

    So I’ve been using the web interface to report on downloads and revenue for years, but now I’m trying to switch over to using the API to make my life MUCH easier in the long run. However, it seems that in the web interface, the “downloads” reported are actually net downloads (total downloads less returns), and similarly with the revenue. The problem is that in this response data above for the “SALES BY DATE AND PRODUCT REPORT,” there is no “net_revenue” which would go with the net_downloads. Is there a work-around that I’m missing?

  • Mathijas

    I have a humble request to update examples, becouse they do not match with the specification (i.e. group by product,country/products+countries, the product object in json does not exist, only product_id etc.). Thanks in advance!

    I am downloading sales reports for apps with group_by=country,product,store. If I use include_inapps=true then I get whole table of apps and its’ inapps. When I set it to false, then I get only apps without inapp information.

    The question is, is it possible to sum up revenue from application and its’ inapps? I.e. include_inapps=sum_to_parent_product. Thanks for reply.

  • Jonathan

    Is it possible to get the cumulated downloads on a specific day?

    • alexquick

      Yup, “https://api.appfigures.com/v2/reports/sales?group_by=date&start_date=2014-10-09&end_date=2014-10-09” to get daily totals for yesterday

  • Marc Schopper

    I am confused about the /reports/… resource endpoints. Did you move all the /sales/… resources to /reports/sales/… ?

    Trying to access GET https://api.appfigures.com/v2/reports/sales/regions

    responds with an 404

  • easy

    i have a problem getting the all time totals –

    shouldn’t the route api.appfigures.com/v2/sales?products=[myproductId} provide me this values?

    when I compare the values I get with the values presented by the appfigures user interface they are much to low

    • easy

      to be clearer: especially the values from the iOS apps are not matching the real values presented on the appfigures site

      • appfigures

        If the app in question has IAPs you’ll also need to have those product ids in the request and sum them all up.

        If it isn’t the issue however please get in touch directly. We’re not seeing this issue on our end.

  • Erdem Inan

    We are getting sales data by product id, I could not exactly match product id for each sales with the corresponding app names, (I tried to do it manually by getting an export from appfigures interface and have a table that matches the product ids in the sales by product section of the interface to the product ids in the sales table, but could not match them correctly.

    Am I missing something? Is there an additional product ids that we need to look for to make the connection between two info?

    • appfigures

      Are you by any chance comparing v1.1 ids to v2 ids? We changed the id system in v2.

      You can easily list your products and their ids by loading /products/mine.

      FYI – Since the site’s export uses v2 those should match.

  • olikingshott

    I get reports with 0 downloads until the reports have been released – is there a way to differentiate between no downloads / unreleased reports?

  • PocketLogic

    I have noticed that when getting sales by date, and granularity=daily, that requesting a date range that is more than thirty days, the server returns an HTTP status of 400. In other words, it seems you can only get 30 days of data at a time. I didn’t see reference to this in the above docs – is this intentional or just not documented?

    • appfigures

      That’s correct, the daily granularity is limited to 30 days and weekly to one year.

      Thanks for pointing this out! We corrected the documentation.

      • Jeff Brown

        Is it true that you can only get the prior 30 days? Not just API requests for ANY 30 days?

        • appfigures

          Nope. You can get any 30 day period at one time. if you need a longer period you can simply make multiple calls.

          • Jeff Brown

            I’m having trouble seeing this for the apple store. I can get older figures for google play and amazon just fine, but prior to 30 days ago, I see nothing for apple. Could this be because I’ve only recently signed up for the service?

          • appfigures

            Keep in mind that the API will provide access to data that’s in your account, so if there’s no data for a particular range of dates in your account you won’t be able to access that data via the API.

            Apple limits the amount of information we can retrieve so if you signed up recently there’s a limit on the number of daily reports we can get. Moving forward however we retain all import data.

            Contact us directly if you’d like more details about this.